1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2024-11-26 07:19:22 +00:00

Handle pre-flight requests

This commit is contained in:
Pierre HUBERT 2021-03-14 14:25:11 +01:00
parent c29d8b1997
commit eb0c7aec6a
5 changed files with 57 additions and 30 deletions

View File

@ -13,9 +13,6 @@ server-port: 3000
# This value is used to trust upstream proxy # This value is used to trust upstream proxy
proxy: "127.0.0.1" proxy: "127.0.0.1"
# If set to true Access-Control-Allow-Origin will be set for https
force-https: true
# User data storage # User data storage
storage-url: http://devweb.local/comunic/current/user_data/ storage-url: http://devweb.local/comunic/current/user_data/
storage-path: /home/pierre/Documents/projets_web/comunic/current/user_data/ storage-path: /home/pierre/Documents/projets_web/comunic/current/user_data/

View File

@ -35,7 +35,6 @@ pub struct Config {
pub play_store_url: String, pub play_store_url: String,
pub android_direct_download_url: String, pub android_direct_download_url: String,
pub proxy: Option<String>, pub proxy: Option<String>,
pub force_https: bool,
pub verbose_mode: bool, pub verbose_mode: bool,
pub database: DatabaseConfig, pub database: DatabaseConfig,
pub rtc_relay: Option<RtcRelayConfig>, pub rtc_relay: Option<RtcRelayConfig>,
@ -116,8 +115,6 @@ impl Config {
s => Some(s.to_string()) s => Some(s.to_string())
}, },
force_https: Config::yaml_bool(parsed, "force-https"),
verbose_mode: Config::yaml_bool(parsed, "verbose-mode"), verbose_mode: Config::yaml_bool(parsed, "verbose-mode"),
database: database_conf, database: database_conf,

View File

@ -7,13 +7,13 @@ use actix_web::http::{HeaderName, HeaderValue, StatusCode};
use serde::Serialize; use serde::Serialize;
use crate::api_data::http_error::HttpError; use crate::api_data::http_error::HttpError;
use crate::routes::RequestResult;
use crate::data::api_client::APIClient; use crate::data::api_client::APIClient;
use crate::data::base_request_handler::{BaseRequestHandler, RequestValue}; use crate::data::base_request_handler::{BaseRequestHandler, RequestValue};
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::error::{Res, ResultBoxError}; use crate::data::error::{Res, ResultBoxError};
use crate::data::user_token::UserAccessToken; use crate::data::user_token::UserAccessToken;
use crate::helpers::{account_helper, api_helper}; use crate::helpers::{account_helper, api_helper};
use crate::routes::RequestResult;
/// Http request handler /// Http request handler
/// ///
@ -86,22 +86,17 @@ impl HttpRequestHandler {
)?; )?;
if let Some(domain) = &client.domain { if let Some(allowed_origin) = &client.domain {
let allowed_origin = match conf().force_https {
true => format!("https://{}", domain),
false => format!("http://{}", domain)
};
match self.request.headers().get("Referer") { match self.request.headers().get("Referer") {
None => self.bad_request("Unknown origin!".to_string())?, None => self.bad_request("Unknown origin!".to_string())?,
Some(s) => { 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.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); self.client = Some(client);

View File

@ -1,23 +1,34 @@
use crate::constants::database_tables_names::CLIENTS_TABLE; use crate::constants::database_tables_names::CLIENTS_TABLE;
use crate::data::api_client::APIClient; 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; use crate::helpers::database::QueryInfo;
/// API helper /// API helper
/// ///
/// @author Pierre Hubert /// @author Pierre Hubert
/// Get information about a client /// Get information about a client, based on its token
pub fn get_client(name: &str) -> ResultBoxError<APIClient> { pub fn get_client(name: &str) -> ResultBoxError<APIClient> {
QueryInfo::new(CLIENTS_TABLE) QueryInfo::new(CLIENTS_TABLE)
.cond("name", name) .cond("name", name)
.query_row(|res| { .query_row(db_to_client)
Ok(APIClient { }
id: res.get_u64("id")?,
name: res.get_str("name")?,
domain: res.get_optional_str("domain")?, /// Get information about a client, based on its origin
comment: res.get_optional_str("comment")?, pub fn get_by_origin(name: &str) -> ResultBoxError<APIClient> {
default_expiration_time: res.get_u64("default_expiration_time")?, QueryInfo::new(CLIENTS_TABLE)
}) .cond("domain", name)
}) .query_row(db_to_client)
}
fn db_to_client(res: &database::RowResult) -> Res<APIClient> {
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")?,
})
} }

View File

@ -4,7 +4,7 @@ use std::pin::Pin;
use actix_web::{App, FromRequest, http, HttpMessage, HttpRequest, HttpResponse, HttpServer, web}; use actix_web::{App, FromRequest, http, HttpMessage, HttpRequest, HttpResponse, HttpServer, web};
use actix_web::dev::{Decompress, Payload, PayloadStream}; use actix_web::dev::{Decompress, Payload, PayloadStream};
use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, PayloadError}; 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 encoding_rs::UTF_8;
use futures::{FutureExt, Stream, StreamExt}; use futures::{FutureExt, Stream, StreamExt};
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
@ -14,12 +14,12 @@ use percent_encoding::percent_decode_str;
use crate::api_data::http_error::HttpError; use crate::api_data::http_error::HttpError;
use crate::constants::MAX_REQUEST_SIZE; use crate::constants::MAX_REQUEST_SIZE;
use crate::controllers::{rtc_relay_controller, user_ws_controller}; 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::base_request_handler::{BaseRequestHandler, PostFile, RequestValue};
use crate::data::config::Config; use crate::data::config::Config;
use crate::data::http_request_handler::HttpRequestHandler; 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 /// 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 /// Given the configuration, start the server
pub async fn start_server(conf: &Config) -> std::io::Result<()> { 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 // RTC Relay WebSocket route
.service(actix_web::web::resource("/rtc_proxy/ws").to(rtc_relay_controller::open_ws)) .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 // API routes
.route("**", web::get().to(process_request)) .route("**", web::get().to(process_request))
.route("**", web::post().to(process_request)) .route("**", web::post().to(process_request))