Debug file server issues

This commit is contained in:
Niels Andriesse 2021-04-27 09:12:07 +10:00
parent 2fa07705a6
commit 564357e45d
3 changed files with 54 additions and 35 deletions

View File

@ -14,6 +14,7 @@ use warp::{http::StatusCode, reply::Reply, reply::Response, Rejection};
use super::crypto;
use super::errors::Error;
use super::models;
use super::rpc;
use super::storage;
enum AuthorizationLevel {
@ -124,15 +125,21 @@ pub fn get_all_rooms() -> Result<Response, Rejection> {
// Files
pub async fn store_file(
base64_encoded_bytes: &str, auth_token: &str, pool: &storage::DatabaseConnectionPool,
base64_encoded_bytes: &str, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool,
) -> Result<Response, Rejection> {
// It'd be nice to use the UUID crate for the file ID, but clients want an integer ID
let now = chrono::Utc::now().timestamp_nanos();
// Check authorization level
let (has_authorization_level, _) =
has_authorization_level(auth_token, AuthorizationLevel::Basic, pool)?;
if !has_authorization_level {
return Err(warp::reject::custom(Error::Unauthorized));
// Check authorization level if needed
match rpc::MODE {
rpc::Mode::OpenGroupServer => {
let auth_token = auth_token.ok_or(warp::reject::custom(Error::NoAuthToken))?;
let (has_authorization_level, _) =
has_authorization_level(&auth_token, AuthorizationLevel::Basic, pool)?;
if !has_authorization_level {
return Err(warp::reject::custom(Error::Unauthorized));
}
}
rpc::Mode::FileServer => { /* Do nothing */ }
}
// Parse bytes
let bytes = match base64::decode(base64_encoded_bytes) {
@ -184,14 +191,20 @@ pub async fn store_file(
}
pub async fn get_file(
id: i64, auth_token: &str, pool: &storage::DatabaseConnectionPool,
id: i64, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool,
) -> Result<GenericStringResponse, Rejection> {
// Doesn't return a response directly for testing purposes
// Check authorization level
let (has_authorization_level, _) =
has_authorization_level(auth_token, AuthorizationLevel::Basic, pool)?;
if !has_authorization_level {
return Err(warp::reject::custom(Error::Unauthorized));
// Check authorization level if needed
match rpc::MODE {
rpc::Mode::OpenGroupServer => {
let auth_token = auth_token.ok_or(warp::reject::custom(Error::NoAuthToken))?;
let (has_authorization_level, _) =
has_authorization_level(&auth_token, AuthorizationLevel::Basic, pool)?;
if !has_authorization_level {
return Err(warp::reject::custom(Error::Unauthorized));
}
}
rpc::Mode::FileServer => { /* Do nothing */ }
}
// Try to read the file
let mut bytes = vec![];

View File

@ -10,7 +10,7 @@ use super::models;
use super::storage;
#[allow(dead_code)]
enum Mode {
pub enum Mode {
FileServer,
OpenGroupServer,
}
@ -23,7 +23,7 @@ pub struct RpcCall {
pub headers: HashMap<String, String>,
}
const MODE: Mode = Mode::OpenGroupServer;
pub const MODE: Mode = Mode::OpenGroupServer;
pub async fn handle_rpc_call(rpc_call: RpcCall) -> Result<Response, Rejection> {
// Check that the endpoint is a valid URI and deconstruct it into a path
@ -69,6 +69,7 @@ async fn handle_get_request(
) -> Result<Response, Rejection> {
// Handle routes that don't require authorization first
if path == "auth_token_challenge" {
reject_if_file_server_mode(path)?;
let pool = get_pool_for_room(&rpc_call)?;
let challenge = handlers::get_auth_token_challenge(query_params, &pool)?;
#[derive(Debug, Deserialize, Serialize)]
@ -94,9 +95,7 @@ async fn handle_get_request(
return Err(warp::reject::custom(Error::InvalidRpcCall));
}
}
// Check that the auth token is present
let auth_token = auth_token.ok_or(warp::reject::custom(Error::NoAuthToken))?;
// Switch on the path
// This route requires auth in open group server mode, but not in file server mode
let pool = get_pool_for_room(&rpc_call)?;
if path.starts_with("files") {
let components: Vec<&str> = path.split("/").collect(); // Split on subsequent slashes
@ -111,10 +110,13 @@ async fn handle_get_request(
return Err(warp::reject::custom(Error::InvalidRpcCall));
}
};
return handlers::get_file(file_id, &auth_token, &pool)
return handlers::get_file(file_id, auth_token, &pool)
.await
.map(|json| warp::reply::json(&json).into_response());
}
// Check that the auth token is present
let auth_token = auth_token.ok_or(warp::reject::custom(Error::NoAuthToken))?;
// Switch on the path
match path {
"messages" => {
reject_if_file_server_mode(path)?;
@ -187,9 +189,24 @@ async fn handle_post_request(
};
return handlers::compact_poll(wrapper.requests);
}
// This route requires auth in open group server mode, but not in file server mode
let pool = get_pool_for_room(&rpc_call)?;
if path == "files" {
#[derive(Debug, Deserialize)]
struct JSON {
file: String,
}
let json: JSON = match serde_json::from_str(&rpc_call.body) {
Ok(message) => message,
Err(e) => {
warn!("Couldn't parse JSON from: {} due to error: {}.", rpc_call.body, e);
return Err(warp::reject::custom(Error::InvalidRpcCall));
}
};
return handlers::store_file(&json.file, auth_token, &pool).await;
}
// Check that the auth token is present
let auth_token = auth_token.ok_or(warp::reject::custom(Error::NoAuthToken))?;
let pool = get_pool_for_room(&rpc_call)?;
// Switch on the path
if path.starts_with("rooms") {
reject_if_file_server_mode(path)?;
@ -241,6 +258,7 @@ async fn handle_post_request(
return handlers::ban(&json.public_key, &auth_token, &pool);
}
"claim_auth_token" => {
reject_if_file_server_mode(path)?;
#[derive(Debug, Deserialize)]
struct JSON {
public_key: String,
@ -254,20 +272,6 @@ async fn handle_post_request(
};
return handlers::claim_auth_token(&json.public_key, &auth_token, &pool);
}
"files" => {
#[derive(Debug, Deserialize)]
struct JSON {
file: String,
}
let json: JSON = match serde_json::from_str(&rpc_call.body) {
Ok(message) => message,
Err(e) => {
warn!("Couldn't parse JSON from: {} due to error: {}.", rpc_call.body, e);
return Err(warp::reject::custom(Error::InvalidRpcCall));
}
};
return handlers::store_file(&json.file, &auth_token, &pool).await;
}
_ => {
warn!("Ignoring RPC call with invalid or unused endpoint: {}.", path);
return Err(warp::reject::custom(Error::InvalidRpcCall));
@ -310,6 +314,7 @@ fn handle_delete_request(
}
// DELETE /auth_token
if path == "auth_token" {
reject_if_file_server_mode(path)?;
return handlers::delete_auth_token(&auth_token, pool);
}
// Unrecognized endpoint

View File

@ -82,13 +82,14 @@ async fn test_file_handling() {
// Get an auth token
let (auth_token, _) = get_auth_token();
// Store the test file
handlers::store_file(TEST_FILE, &auth_token, &pool).await.unwrap();
handlers::store_file(TEST_FILE, Some(auth_token.clone()), &pool).await.unwrap();
// Check that there's a file record
let conn = pool.get().unwrap();
let raw_query = format!("SELECT id FROM {}", storage::FILES_TABLE);
let id: i64 = conn.query_row(&raw_query, params![], |row| Ok(row.get(0)?)).unwrap();
// Retrieve the file and check the content
let base64_encoded_file = handlers::get_file(id, &auth_token, &pool).await.unwrap().result;
let base64_encoded_file =
handlers::get_file(id, Some(auth_token.clone()), &pool).await.unwrap().result;
assert_eq!(base64_encoded_file, TEST_FILE);
// Prune the file and check that it's gone
// Will evaluate to now + 60