1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2025-01-07 11:22:36 +00:00
comunicapiv3/src/data/http_request_handler.rs

247 lines
7.5 KiB
Rust
Raw Normal View History

2020-05-21 13:28:07 +00:00
use actix_web::{web, HttpRequest, HttpResponse};
use crate::controllers::routes::RequestResult;
use std::error::Error;
use serde::Serialize;
2020-05-23 08:19:15 +00:00
use crate::data::error::{ResultBoxError, ExecError};
2020-05-23 07:37:21 +00:00
use std::collections::HashMap;
2020-05-24 15:57:47 +00:00
use crate::helpers::{api_helper, account_helper};
2020-05-23 12:08:22 +00:00
use actix_web::http::{HeaderName, HeaderValue};
use std::str::FromStr;
use crate::data::config::conf;
use crate::data::api_client::APIClient;
2020-05-24 11:09:50 +00:00
use crate::api_data::http_error::HttpError;
2020-05-24 15:57:47 +00:00
use crate::data::user::UserID;
2020-05-21 13:28:07 +00:00
/// Http request handler
///
/// @author Pierre Hubert
2020-05-23 07:37:21 +00:00
/// Single request body value
pub struct RequestValue {
pub string: Option<String>
}
impl RequestValue {
/// Build a string value
pub fn string(s: String) -> RequestValue {
RequestValue {
string: Some(s)
}
}
}
2020-05-21 13:28:07 +00:00
#[derive(Serialize)]
struct SuccessMessage {
success: String
}
pub struct HttpRequestHandler {
request: web::HttpRequest,
2020-05-23 07:37:21 +00:00
body: HashMap<String, RequestValue>,
response: Option<web::HttpResponse>,
2020-05-23 12:08:22 +00:00
headers: HashMap<String, String>,
client: Option<APIClient>,
2020-05-24 15:57:47 +00:00
curr_user_id: Option<UserID>,
2020-05-21 13:28:07 +00:00
}
impl HttpRequestHandler {
/// Construct a new request handler
2020-05-23 07:37:21 +00:00
pub fn new(req: HttpRequest, body: HashMap<String, RequestValue>) -> HttpRequestHandler {
2020-05-21 13:28:07 +00:00
HttpRequestHandler {
request: req,
2020-05-23 07:37:21 +00:00
body,
response: None,
2020-05-23 12:08:22 +00:00
headers: HashMap::new(),
client: None,
2020-05-24 15:57:47 +00:00
curr_user_id: None,
2020-05-21 13:28:07 +00:00
}
}
/// Check if a response has been set for this request
pub fn has_response(&self) -> bool {
self.response.is_some()
}
2020-05-23 07:54:13 +00:00
/// Get the response status code, eg. 200 or 404
pub fn response_status_code(&self) -> u16 {
self.response.as_ref().unwrap().status().as_u16()
}
2020-05-21 13:28:07 +00:00
/// Take the response from this struct
2020-05-23 12:08:22 +00:00
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)
2020-05-21 13:28:07 +00:00
}
2020-05-23 17:17:48 +00:00
/// Set request response
pub fn set_response<T: Serialize>(&mut self, data: T) -> RequestResult {
self.response = Some(HttpResponse::Ok().json(data));
Ok(())
}
2020-05-21 13:28:07 +00:00
/// Success message
pub fn success(&mut self, message: &str) -> RequestResult {
self.response = Some(HttpResponse::Ok().json(SuccessMessage {
success: message.to_string()
}));
Ok(())
}
2020-05-23 17:17:48 +00:00
/// Internal error response (500)
2020-05-21 13:28:07 +00:00
pub fn internal_error(&mut self, error: Box<dyn Error>) -> RequestResult {
self.response = Some(HttpResponse::InternalServerError().json(
HttpError::internal_error("Internal server error.")));
2020-05-23 08:19:15 +00:00
Err(error)
2020-05-21 13:28:07 +00:00
}
2020-05-23 07:37:21 +00:00
2020-05-23 17:17:48 +00:00
/// Bad request (400)
2020-05-23 08:14:21 +00:00
pub fn bad_request(&mut self, message: String) -> RequestResult {
self.response = Some(HttpResponse::BadRequest().json(
HttpError::bad_request(&message)));
2020-05-23 08:19:15 +00:00
Err(Box::new(ExecError::new(&message)))
2020-05-23 08:14:21 +00:00
}
2020-05-23 17:17:48 +00:00
/// Forbidden (401)
pub fn forbidden(&mut self, message: String) -> RequestResult {
self.response = Some(HttpResponse::Forbidden().json(
HttpError::forbidden(&message)));
Err(Box::new(ExecError::new(&message)))
}
2020-05-23 09:00:53 +00:00
/// If result is not OK, return a bad request
pub fn ok_or_bad_request<E>(&mut self, res: ResultBoxError<E>, msg: &str) -> ResultBoxError<E> {
match res {
Ok(e) => Ok(e),
Err(err) => {
println!("Error leading to bad request: {}", err);
self.bad_request(msg.to_string())?;
unreachable!()
2020-05-23 12:08:22 +00:00
}
2020-05-23 09:00:53 +00:00
}
}
2020-05-23 07:54:13 +00:00
/// Get the path of the request
pub fn request_path(&self) -> String {
self.request.path().to_string()
}
2020-05-23 07:37:21 +00:00
2020-05-23 17:17:48 +00:00
/// Get information about the client which made the request
pub fn api_client(&self) -> &APIClient {
self.client.as_ref().unwrap()
}
2020-05-23 07:37:21 +00:00
/// Check if a POST parameter was present in the request or not
pub fn has_post_parameter(&self, name: &str) -> bool {
self.body.contains_key(name)
}
/// Get a post parameter
2020-05-23 08:14:21 +00:00
pub fn post_parameter(&mut self, name: &str) -> ResultBoxError<&RequestValue> {
if !self.has_post_parameter(name) {
self.bad_request(format!("POST parameter '{}' not found in request!", name))?;
}
Ok(self.body.get(name).unwrap())
2020-05-23 07:37:21 +00:00
}
/// Get a post string
2020-05-23 08:19:15 +00:00
pub fn post_string(&mut self, name: &str) -> ResultBoxError<String> {
2020-05-23 15:09:28 +00:00
self.post_string_opt(name, 1, true)
}
/// Get a post string, specifying minimum length
pub fn post_string_opt(&mut self, name: &str, min_length: usize, required: bool)
-> ResultBoxError<String> {
2020-05-23 07:37:21 +00:00
let param = self.post_parameter(name)?;
2020-05-23 15:09:28 +00:00
match (&param.string, required) {
(None, true) =>
Err(self.bad_request(format!("'{}' is not a string!", name)).unwrap_err()),
(None, false) => Ok(String::new()),
(Some(s), _) => {
if s.len() >= min_length {
Ok(s.to_string())
} else {
Err(self.bad_request(format!("'{}' is too short!", name)).unwrap_err())
}
2020-05-23 09:00:53 +00:00
}
2020-05-23 07:37:21 +00:00
}
}
2020-05-23 09:00:53 +00:00
2020-05-24 15:57:47 +00:00
/// Check API client tokens
pub fn check_client_token(&mut self) -> RequestResult {
2020-05-23 09:00:53 +00:00
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),
2020-05-23 12:08:22 +00:00
"Client not recognized!",
2020-05-23 09:00:53 +00:00
)?;
2020-05-23 12:08:22 +00:00
if let Some(domain) = &client.domain {
2020-05-23 15:09:28 +00:00
let allowed_origin = match conf().force_https {
2020-05-23 12:08:22 +00:00
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())?;
}
2020-05-23 15:09:28 +00:00
}
2020-05-23 12:08:22 +00:00
}
self.headers.insert("Access-Control-Allow-Origin".to_string(), allowed_origin);
}
self.client = Some(client);
2020-05-23 09:00:53 +00:00
Ok(())
}
2020-05-23 15:09:28 +00:00
2020-05-24 15:57:47 +00:00
/// Check login token
pub fn check_user_token(&mut self) -> RequestResult {
let token = self.post_string("userToken1")?;
// Find user
let user_id = self.ok_or_bad_request(
account_helper::get_user_by_login_token(&token, self.api_client()),
"Please check your login tokens!")?;
self.curr_user_id = Some(user_id);
Ok(())
}
/// Get user ID. This function assess that a user ID is available to continue
pub fn user_id(&self) -> ResultBoxError<UserID> {
match self.curr_user_id {
Some(s) => Ok(s),
None => Err(ExecError::boxed_new("Could not get required user ID!"))
}
}
2020-05-23 15:09:28 +00:00
/// Get an email included in the request
pub fn post_email(&mut self, name: &str) -> ResultBoxError<String> {
let mail = self.post_string(name)?;
if !mailchecker::is_valid(&mail) {
self.bad_request("Invalid email address!".to_string())?;
}
Ok(mail)
}
2020-05-21 13:28:07 +00:00
}