From 505fa5adb329e4c34d3aae19e763ece6b88634ad Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sat, 23 May 2020 14:08:22 +0200 Subject: [PATCH] Check client tokens --- config.yaml | 2 +- src/controllers/routes.rs | 2 +- src/controllers/server.rs | 9 +++++- src/data/http_error.rs | 27 ++++++++++++------ src/data/http_request_handler.rs | 48 ++++++++++++++++++++++++++++---- src/helpers/api_helper.rs | 2 +- src/helpers/database.rs | 43 ++++++++++++++++++---------- 7 files changed, 99 insertions(+), 34 deletions(-) diff --git a/config.yaml b/config.yaml index 59b76eb..0fee6db 100644 --- a/config.yaml +++ b/config.yaml @@ -15,7 +15,7 @@ server-port: 3000 proxy: none # If set to true Access-Control-Allow-Origin will be set for https -force-https: false +force-https: true # User data storage storage-url: https://devweb.local/comunic/current/user_data/ diff --git a/src/controllers/routes.rs b/src/controllers/routes.rs index f89232f..c839b74 100644 --- a/src/controllers/routes.rs +++ b/src/controllers/routes.rs @@ -61,6 +61,6 @@ pub fn get_routes() -> Vec { // Account controller Route::post_without_login("/account/login", Box::new(account_controller::login_user)), - Route::post_without_login("/account/connectUSER", Box::new(account_controller::login_user)), + Route::post_without_login("/user/connectUSER", Box::new(account_controller::login_user)), ] } \ No newline at end of file diff --git a/src/controllers/server.rs b/src/controllers/server.rs index 87209a0..39d01e3 100644 --- a/src/controllers/server.rs +++ b/src/controllers/server.rs @@ -172,7 +172,14 @@ async fn process_request(custom_req: CustomRequest) -> HttpResponse { } } - request.response() + // Send the response + match request.response() { + Ok(s) => s, + Err(e) => { + println!("Error while getting response: {}", e); + HttpResponse::InternalServerError().body("Response error") + } + } } diff --git a/src/data/http_error.rs b/src/data/http_error.rs index 81deede..1334a54 100644 --- a/src/data/http_error.rs +++ b/src/data/http_error.rs @@ -3,36 +3,45 @@ use serde::Serialize; /// HTTP request error /// /// @author Pierre Hubert - #[derive(Serialize)] -pub struct HttpError { +pub struct InnerHTTPError { pub code: u16, pub message: String, } -impl HttpError { +#[derive(Serialize)] +pub struct HttpError { + pub error: InnerHTTPError +} +impl HttpError { /// Generate a 404 error pub fn not_found(message: &str) -> HttpError { HttpError { - code: 404, - message: message.to_string() + error: InnerHTTPError { + code: 404, + message: message.to_string(), + } } } /// Generate a 500 error pub fn internal_error(message: &str) -> HttpError { HttpError { - code: 500, - message: message.to_string() + error: InnerHTTPError { + code: 500, + message: message.to_string(), + } } } /// Generate a 401 error pub fn bad_request(message: &str) -> HttpError { HttpError { - code: 401, - message: message.to_string() + error: InnerHTTPError { + code: 401, + message: message.to_string(), + } } } } \ No newline at end of file diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index 1125819..755760f 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -6,6 +6,10 @@ use serde::Serialize; use crate::data::error::{ResultBoxError, ExecError}; use std::collections::HashMap; use crate::helpers::api_helper; +use actix_web::http::{HeaderName, HeaderValue}; +use std::str::FromStr; +use crate::data::config::conf; +use crate::data::api_client::APIClient; /// Http request handler /// @@ -34,6 +38,8 @@ pub struct HttpRequestHandler { request: web::HttpRequest, body: HashMap, response: Option, + headers: HashMap, + client: Option, } impl HttpRequestHandler { @@ -43,6 +49,8 @@ impl HttpRequestHandler { request: req, body, response: None, + headers: HashMap::new(), + client: None, } } @@ -57,8 +65,17 @@ impl HttpRequestHandler { } /// Take the response from this struct - pub fn response(self) -> HttpResponse { - self.response.unwrap() + 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) } /// Success message @@ -91,7 +108,7 @@ impl HttpRequestHandler { println!("Error leading to bad request: {}", err); self.bad_request(msg.to_string())?; unreachable!() - }, + } } } @@ -133,11 +150,30 @@ impl HttpRequestHandler { let client = self.ok_or_bad_request( api_helper::get_client(&api_name, &api_token), - "Client not recognized!" + "Client not recognized!", )?; - // TODO : continue here - println!("{:#?}", client); + + 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(()) } } \ No newline at end of file diff --git a/src/helpers/api_helper.rs b/src/helpers/api_helper.rs index 62f065c..3da4c54 100644 --- a/src/helpers/api_helper.rs +++ b/src/helpers/api_helper.rs @@ -20,7 +20,7 @@ pub fn get_client(name: &str, token: &str) -> ResultBoxError { id: res.get_int64("id")? as u32, name: res.get_str("service_name")?, token: res.get_str("token")?, - domain: res.get_optional_str("domain")?, + domain: res.get_optional_str("client_domain")?, }) } ) diff --git a/src/helpers/database.rs b/src/helpers/database.rs index 6af9ef1..25e29c3 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -3,11 +3,11 @@ use std::error::Error; use std::ops::Add; use std::sync::{Arc, Mutex}; -use mysql::{Binary, Pool, ResultSet}; +use mysql::{Binary, Pool, ResultSet, Value}; use mysql::prelude::Queryable; use crate::data::config::DatabaseConfig; -use crate::data::error::ExecError; +use crate::data::error::{ExecError, ResultBoxError}; /// Database access helper /// @@ -107,30 +107,43 @@ impl<'a> RowResult<'a> { } /// Find an integer included in the request - pub fn get_int64(&self, name: &str) -> Result { - let value: Option = self.row.get(self.find_col(name)?); + pub fn get_int64(&self, name: &str) -> Result> { + let value = self.row.get_opt(self.find_col(name)?); match value { - None => Err(ExecError(format!("Could not extract integer field {} !", name))), - Some(s) => Ok(s) + None => Err(ExecError::boxed_string( + format!("Could not extract integer field {} !", name))), + Some(s) => Ok(s?) } } /// Find a string included in the request - pub fn get_str(&self, name: &str) -> Result { - let value: Option = self.row.get(self.find_col(name)?); + pub fn get_str(&self, name: &str) -> Result> { + let value = self.row.get_opt(self.find_col(name)?); match value { - None => Err(ExecError(format!("Could not extract string field {} !", name))), - Some(s) => Ok(s) + None => Err(ExecError::boxed_string( + format!("Could not extract string field {} !", name))), + Some(s) => Ok(s?) } } - /// Get an optional string - pub fn get_optional_str(&self, name: &str) -> Result, ExecError> { - match self.find_col(name) { - Ok(col) => Ok(self.row.get(col)), - Err(_) => Ok(None), + /// Check out whether a given value is null or not + pub fn is_null(&self, name: &str) -> ResultBoxError { + let value = self.row.get_opt(self.find_col(name)?); + + match value { + None => Ok(true), + Some(Ok(Value::NULL)) => Ok(true), + _ => Ok(false) + } + } + + /// Get an optional string => Set to None if string is null + pub fn get_optional_str(&self, name: &str) -> ResultBoxError> { + match self.is_null(name)? { + true => Ok(None), + false => Ok(Some(self.get_str(name)?)) } } }