From eb0c7aec6af68f92e8320e7b26fcd2f7b5a094f3 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sun, 14 Mar 2021 14:25:11 +0100 Subject: [PATCH] Handle pre-flight requests --- config.yaml | 3 --- src/data/config.rs | 3 --- src/data/http_request_handler.rs | 13 ++++-------- src/helpers/api_helper.rs | 33 ++++++++++++++++++++---------- src/server.rs | 35 ++++++++++++++++++++++++++++---- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/config.yaml b/config.yaml index 2cadd92..b67b99d 100644 --- a/config.yaml +++ b/config.yaml @@ -13,9 +13,6 @@ server-port: 3000 # This value is used to trust upstream proxy proxy: "127.0.0.1" -# If set to true Access-Control-Allow-Origin will be set for https -force-https: true - # User data storage storage-url: http://devweb.local/comunic/current/user_data/ storage-path: /home/pierre/Documents/projets_web/comunic/current/user_data/ diff --git a/src/data/config.rs b/src/data/config.rs index 4c5f7f9..f3dfa0b 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -35,7 +35,6 @@ pub struct Config { pub play_store_url: String, pub android_direct_download_url: String, pub proxy: Option, - pub force_https: bool, pub verbose_mode: bool, pub database: DatabaseConfig, pub rtc_relay: Option, @@ -116,8 +115,6 @@ impl Config { s => Some(s.to_string()) }, - force_https: Config::yaml_bool(parsed, "force-https"), - verbose_mode: Config::yaml_bool(parsed, "verbose-mode"), database: database_conf, diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index 2c4f39a..bdf11d2 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -7,13 +7,13 @@ use actix_web::http::{HeaderName, HeaderValue, StatusCode}; use serde::Serialize; use crate::api_data::http_error::HttpError; -use crate::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}; +use crate::routes::RequestResult; /// Http request handler /// @@ -86,22 +86,17 @@ impl HttpRequestHandler { )?; - if let Some(domain) = &client.domain { - let allowed_origin = match conf().force_https { - true => format!("https://{}", domain), - false => format!("http://{}", domain) - }; - + if let Some(allowed_origin) = &client.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) { + 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.headers.insert("Access-Control-Allow-Origin".to_string(), allowed_origin.to_string()); } self.client = Some(client); diff --git a/src/helpers/api_helper.rs b/src/helpers/api_helper.rs index 876d14c..7d7ab8e 100644 --- a/src/helpers/api_helper.rs +++ b/src/helpers/api_helper.rs @@ -1,23 +1,34 @@ use crate::constants::database_tables_names::CLIENTS_TABLE; use crate::data::api_client::APIClient; -use crate::data::error::ResultBoxError; +use crate::data::error::{Res, ResultBoxError}; +use crate::helpers::database; use crate::helpers::database::QueryInfo; /// API helper /// /// @author Pierre Hubert -/// Get information about a client +/// Get information about a client, based on its token pub fn get_client(name: &str) -> ResultBoxError { QueryInfo::new(CLIENTS_TABLE) .cond("name", name) - .query_row(|res| { - Ok(APIClient { - id: res.get_u64("id")?, - name: res.get_str("name")?, - domain: res.get_optional_str("domain")?, - comment: res.get_optional_str("comment")?, - default_expiration_time: res.get_u64("default_expiration_time")?, - }) - }) + .query_row(db_to_client) +} + + +/// Get information about a client, based on its origin +pub fn get_by_origin(name: &str) -> ResultBoxError { + QueryInfo::new(CLIENTS_TABLE) + .cond("domain", name) + .query_row(db_to_client) +} + +fn db_to_client(res: &database::RowResult) -> Res { + Ok(APIClient { + id: res.get_u64("id")?, + name: res.get_str("name")?, + domain: res.get_optional_str("domain")?, + comment: res.get_optional_str("comment")?, + default_expiration_time: res.get_u64("default_expiration_time")?, + }) } \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index d7730ae..4966132 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use actix_web::{App, FromRequest, http, HttpMessage, HttpRequest, HttpResponse, HttpServer, web}; use actix_web::dev::{Decompress, Payload, PayloadStream}; use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, PayloadError}; -use actix_web::web::{Bytes, BytesMut, BufMut, Buf}; +use actix_web::web::{Buf, BufMut, Bytes, BytesMut}; use encoding_rs::UTF_8; use futures::{FutureExt, Stream, StreamExt}; use futures::future::LocalBoxFuture; @@ -14,12 +14,12 @@ use percent_encoding::percent_decode_str; use crate::api_data::http_error::HttpError; use crate::constants::MAX_REQUEST_SIZE; use crate::controllers::{rtc_relay_controller, user_ws_controller}; -use crate::routes::{get_routes, RequestResult, Route}; -use crate::routes::Method::{GET, POST}; use crate::data::base_request_handler::{BaseRequestHandler, PostFile, RequestValue}; use crate::data::config::Config; use crate::data::http_request_handler::HttpRequestHandler; -use crate::helpers::requests_limit_helper; +use crate::helpers::{api_helper, requests_limit_helper}; +use crate::routes::{get_routes, RequestResult, Route}; +use crate::routes::Method::{GET, POST}; /// Main server functions /// @@ -301,6 +301,30 @@ async fn process_request(custom_req: CustomRequest) -> HttpResponse { } } +/// Handle pre-flight requests +async fn handle_options_request(r: HttpRequest) -> HttpResponse { + + // Extract origin + let origin = r.headers().get("Origin"); + if origin.is_none() { + return HttpResponse::BadRequest().body("Missing origin header!"); + } + let origin = origin.unwrap().to_str().unwrap_or("").to_string(); + + // Find client + let client = api_helper::get_by_origin(&origin); + if client.is_err() { + eprintln!("Failed to handle OPTIONS request: {:#?}", client); + return HttpResponse::Unauthorized().body("Unkown origin!"); + } + + // Accept request + HttpResponse::NoContent() + .header("Access-Control-Allow-Origin", origin) + .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") + .finish() +} + /// Given the configuration, start the server pub async fn start_server(conf: &Config) -> std::io::Result<()> { @@ -320,6 +344,9 @@ pub async fn start_server(conf: &Config) -> std::io::Result<()> { // RTC Relay WebSocket route .service(actix_web::web::resource("/rtc_proxy/ws").to(rtc_relay_controller::open_ws)) + // Option + .route("**", web::method(http::Method::OPTIONS).to(handle_options_request)) + // API routes .route("**", web::get().to(process_request)) .route("**", web::post().to(process_request))