2021-03-22 03:55:43 +01:00
|
|
|
use std::convert::TryInto;
|
2021-03-19 00:09:13 +01:00
|
|
|
use std::fs;
|
2021-03-23 23:12:54 +01:00
|
|
|
use std::collections::HashMap;
|
2021-03-19 00:09:13 +01:00
|
|
|
use std::io::prelude::*;
|
2021-03-23 04:25:52 +01:00
|
|
|
use std::path::Path;
|
2021-03-19 00:09:13 +01:00
|
|
|
|
2021-03-17 00:10:26 +01:00
|
|
|
use chrono;
|
2021-03-19 06:44:07 +01:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-03-10 03:08:34 +01:00
|
|
|
use rusqlite::params;
|
2021-03-16 06:25:59 +01:00
|
|
|
use rand::{thread_rng, Rng};
|
2021-03-19 00:09:13 +01:00
|
|
|
use uuid::Uuid;
|
2021-03-12 05:46:06 +01:00
|
|
|
use warp::{Rejection, http::StatusCode, reply::Reply, reply::Response};
|
2021-03-10 03:08:34 +01:00
|
|
|
|
2021-03-16 06:25:59 +01:00
|
|
|
use super::crypto;
|
2021-03-12 06:40:24 +01:00
|
|
|
use super::errors::Error;
|
2021-03-10 03:08:34 +01:00
|
|
|
use super::models;
|
|
|
|
use super::storage;
|
|
|
|
|
2021-03-17 23:58:45 +01:00
|
|
|
enum AuthorizationLevel {
|
|
|
|
Basic,
|
|
|
|
Moderator
|
|
|
|
}
|
|
|
|
|
2021-03-22 03:55:43 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct GenericStringResponse {
|
|
|
|
pub result: String
|
|
|
|
}
|
|
|
|
|
2021-03-23 05:22:54 +01:00
|
|
|
// Rooms
|
|
|
|
|
|
|
|
// Currently not exposed
|
|
|
|
pub async fn create_room(id: &str, name: &str) -> Result<Response, Rejection> {
|
|
|
|
// Get a connection
|
|
|
|
let pool = &storage::MAIN_POOL;
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-23 05:27:47 +01:00
|
|
|
// Insert the room
|
|
|
|
let stmt = format!("REPLACE INTO {} (id, name) VALUES (?1, ?2)", storage::MAIN_TABLE);
|
2021-03-23 05:22:54 +01:00
|
|
|
match conn.execute(&stmt, params![ id, name ]) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't create room due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 05:27:47 +01:00
|
|
|
// Set up the database
|
2021-03-23 05:39:42 +01:00
|
|
|
storage::create_database_if_needed(id);
|
2021-03-23 05:22:54 +01:00
|
|
|
// Return
|
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
|
|
|
}
|
|
|
|
|
2021-03-19 00:14:14 +01:00
|
|
|
// Files
|
|
|
|
|
2021-03-23 05:22:54 +01:00
|
|
|
pub async fn store_file(base64_encoded_bytes: &str, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-19 00:09:13 +01:00
|
|
|
// Parse bytes
|
|
|
|
let bytes = match base64::decode(base64_encoded_bytes) {
|
|
|
|
Ok(bytes) => bytes,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't parse bytes from invalid base64 encoding due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Generate UUID
|
|
|
|
let id = Uuid::new_v4();
|
|
|
|
let mut buffer = Uuid::encode_buffer();
|
2021-03-19 01:18:03 +01:00
|
|
|
let id: String = id.to_simple().encode_lower(&mut buffer).to_string();
|
2021-03-19 00:09:13 +01:00
|
|
|
// Update the database
|
|
|
|
// We do this * before * storing the actual file, so that in case something goes
|
|
|
|
// wrong we're not left with files that'll never be pruned.
|
|
|
|
let now = chrono::Utc::now().timestamp();
|
2021-03-19 03:26:53 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-19 00:09:13 +01:00
|
|
|
let stmt = format!("INSERT INTO {} (id, timestamp) VALUES (?1, ?2)", storage::FILES_TABLE);
|
2021-03-19 03:26:53 +01:00
|
|
|
let _ = match conn.execute(&stmt, params![ id, now ]) {
|
2021-03-19 00:09:13 +01:00
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't insert file record due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Write to file
|
|
|
|
let mut pos = 0;
|
2021-03-23 04:25:52 +01:00
|
|
|
let raw_path = format!("files/{}", &id);
|
|
|
|
let path = Path::new(&raw_path);
|
|
|
|
let mut buffer = match fs::File::create(path) {
|
2021-03-19 00:09:13 +01:00
|
|
|
Ok(buffer) => buffer,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't store file due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
while pos < bytes.len() {
|
|
|
|
let count = match buffer.write(&bytes[pos..]) {
|
|
|
|
Ok(count) => count,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't store file due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
pos += count;
|
|
|
|
}
|
|
|
|
// Return
|
2021-03-22 03:55:43 +01:00
|
|
|
let json = GenericStringResponse { result : id };
|
2021-03-19 06:44:07 +01:00
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-19 00:09:13 +01:00
|
|
|
}
|
|
|
|
|
2021-03-22 03:55:43 +01:00
|
|
|
pub async fn get_file(id: &str) -> Result<GenericStringResponse, Rejection> { // Doesn't return a response directly for testing purposes
|
2021-03-19 00:09:13 +01:00
|
|
|
// Check that the ID is a valid UUID
|
|
|
|
match Uuid::parse_str(id) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't parse UUID from: {} due to error: {}.", id, e);
|
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Try to read the file
|
2021-03-23 04:25:52 +01:00
|
|
|
let raw_path = format!("files/{}", id);
|
|
|
|
let path = Path::new(&raw_path);
|
|
|
|
let bytes = match fs::read(path) {
|
2021-03-19 00:09:13 +01:00
|
|
|
Ok(bytes) => bytes,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't read file due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Base64 encode the result
|
|
|
|
let base64_encoded_bytes = base64::encode(bytes);
|
|
|
|
// Return
|
2021-03-22 03:55:43 +01:00
|
|
|
let json = GenericStringResponse { result : base64_encoded_bytes };
|
2021-03-19 06:44:07 +01:00
|
|
|
return Ok(json);
|
2021-03-19 00:09:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Authentication
|
|
|
|
|
2021-03-23 23:12:54 +01:00
|
|
|
pub async fn get_auth_token_challenge(query_params: HashMap<String, String>, pool: &storage::DatabaseConnectionPool) -> Result<models::Challenge, Rejection> { // Doesn't return a response directly for testing purposes
|
|
|
|
// Get the public key
|
|
|
|
let hex_public_key = query_params.get("public_key").ok_or(warp::reject::custom(Error::InvalidRpcCall))?;
|
2021-03-16 06:25:59 +01:00
|
|
|
// Validate the public key
|
2021-03-17 01:51:11 +01:00
|
|
|
if !is_valid_public_key(hex_public_key) {
|
2021-03-23 23:12:54 +01:00
|
|
|
println!("Ignoring challenge request for invalid public key: {}.", hex_public_key);
|
2021-03-16 06:25:59 +01:00
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
|
|
|
// Convert the public key to bytes and cut off the version byte
|
2021-03-22 03:55:43 +01:00
|
|
|
let public_key: [u8; 32] = hex::decode(hex_public_key).unwrap()[1..].try_into().unwrap(); // Safe because we know it has a length of 32 at this point
|
2021-03-16 06:25:59 +01:00
|
|
|
// Generate an ephemeral key pair
|
2021-03-19 04:04:56 +01:00
|
|
|
let (ephemeral_private_key, ephemeral_public_key) = crypto::generate_x25519_key_pair().await;
|
2021-03-16 06:28:54 +01:00
|
|
|
// Generate a symmetric key from the requesting user's public key and the ephemeral private key
|
2021-03-16 06:25:59 +01:00
|
|
|
let symmetric_key = crypto::get_x25519_symmetric_key(&public_key, &ephemeral_private_key).await?;
|
|
|
|
// Generate a random token
|
|
|
|
let mut token = [0u8; 48];
|
|
|
|
thread_rng().fill(&mut token[..]);
|
2021-03-17 00:40:50 +01:00
|
|
|
// Store the (pending) token
|
2021-03-18 00:06:59 +01:00
|
|
|
// Note that a given public key can have multiple pending tokens
|
2021-03-19 03:26:53 +01:00
|
|
|
let now = chrono::Utc::now().timestamp();
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
let stmt = format!("INSERT INTO {} (public_key, timestamp, token) VALUES (?1, ?2, ?3)", storage::PENDING_TOKENS_TABLE);
|
|
|
|
let _ = match conn.execute(&stmt, params![ hex_public_key, now, token.to_vec() ]) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't insert pending token due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
2021-03-17 01:51:11 +01:00
|
|
|
};
|
2021-03-16 06:25:59 +01:00
|
|
|
// Encrypt the token with the symmetric key
|
|
|
|
let ciphertext = crypto::encrypt_aes_gcm(&token, &symmetric_key).await?;
|
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
return Ok(models::Challenge { ciphertext : base64::encode(ciphertext), ephemeral_public_key : base64::encode(ephemeral_public_key.to_bytes()) });
|
2021-03-16 06:25:59 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 01:22:58 +01:00
|
|
|
pub async fn claim_auth_token(public_key: &str, token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-17 00:10:26 +01:00
|
|
|
// Validate the public key
|
|
|
|
if !is_valid_public_key(&public_key) {
|
|
|
|
println!("Ignoring claim token request for invalid public key.");
|
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
|
|
|
// Validate the token
|
2021-03-18 01:22:58 +01:00
|
|
|
let token = token.ok_or(warp::reject::custom(Error::ValidationFailed))?;
|
2021-03-17 05:28:24 +01:00
|
|
|
if hex::decode(&token).is_err() {
|
2021-03-17 00:10:26 +01:00
|
|
|
println!("Ignoring claim token request for invalid token.");
|
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
|
|
|
}
|
2021-03-19 03:26:53 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-17 00:10:26 +01:00
|
|
|
// Get the pending tokens for the given public key
|
2021-03-19 03:26:53 +01:00
|
|
|
let raw_query = format!("SELECT timestamp, token FROM {} WHERE public_key = (?1) AND timestamp > (?2)", storage::PENDING_TOKENS_TABLE);
|
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
let now = chrono::Utc::now().timestamp();
|
|
|
|
let expiration = now - storage::PENDING_TOKEN_EXPIRATION;
|
|
|
|
let rows = match query.query_map(params![ public_key, expiration ], |row| {
|
|
|
|
Ok((row.get(0)?, row.get(1)?))
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't get pending tokens due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
2021-03-17 00:10:26 +01:00
|
|
|
};
|
2021-03-19 03:26:53 +01:00
|
|
|
let pending_tokens: Vec<(i64, Vec<u8>)> = rows.filter_map(|result| result.ok()).collect();
|
2021-03-17 00:10:26 +01:00
|
|
|
// Check that the token being claimed is in fact one of the pending tokens
|
|
|
|
let claim = hex::decode(token).unwrap(); // Safe because we validated it above
|
2021-03-17 00:40:50 +01:00
|
|
|
let index = pending_tokens.iter().position(|(_, pending_token)| *pending_token == claim).ok_or_else(|| Error::Unauthorized)?;
|
|
|
|
let token = &pending_tokens[index].1;
|
2021-03-17 00:10:26 +01:00
|
|
|
// Store the claimed token
|
|
|
|
let stmt = format!("INSERT OR REPLACE INTO {} (public_key, token) VALUES (?1, ?2)", storage::TOKENS_TABLE);
|
2021-03-19 03:26:53 +01:00
|
|
|
match conn.execute(&stmt, params![ public_key, hex::encode(token) ]) {
|
2021-03-17 00:10:26 +01:00
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't insert token due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
}
|
2021-03-19 03:26:53 +01:00
|
|
|
// Delete all pending tokens for the given public key
|
|
|
|
let stmt = format!("DELETE FROM {} WHERE public_key = (?1)", storage::PENDING_TOKENS_TABLE);
|
|
|
|
match conn.execute(&stmt, params![ public_key ]) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => println!("Couldn't delete pending tokens due to error: {}.", e) // It's not catastrophic if this fails
|
|
|
|
};
|
2021-03-17 00:10:26 +01:00
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-16 06:40:51 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 00:29:59 +01:00
|
|
|
pub async fn delete_auth_token(auth_token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-18 00:23:43 +01:00
|
|
|
// Check authorization level
|
|
|
|
let (has_authorization_level, requesting_public_key) = has_authorization_level(auth_token, AuthorizationLevel::Basic, pool).await?;
|
|
|
|
if !has_authorization_level { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-19 03:26:53 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-18 00:23:43 +01:00
|
|
|
// Delete the token
|
|
|
|
let stmt = format!("DELETE FROM {} WHERE public_key = (?1)", storage::TOKENS_TABLE);
|
2021-03-19 03:26:53 +01:00
|
|
|
match conn.execute(&stmt, params![ requesting_public_key ]) {
|
2021-03-18 01:52:39 +01:00
|
|
|
Ok(_) => (),
|
2021-03-18 00:23:43 +01:00
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't delete token due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-18 00:23:43 +01:00
|
|
|
}
|
|
|
|
|
2021-03-19 00:09:13 +01:00
|
|
|
// Message sending & receiving
|
|
|
|
|
2021-03-10 03:08:34 +01:00
|
|
|
/// Inserts the given `message` into the database if it's valid.
|
2021-03-17 23:35:51 +01:00
|
|
|
pub async fn insert_message(mut message: models::Message, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-10 03:08:34 +01:00
|
|
|
// Validate the message
|
2021-03-11 04:20:36 +01:00
|
|
|
if !message.is_valid() {
|
|
|
|
println!("Ignoring invalid message.");
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
2021-03-11 04:20:36 +01:00
|
|
|
}
|
2021-03-17 23:58:45 +01:00
|
|
|
// Check authorization level
|
|
|
|
let (has_authorization_level, requesting_public_key) = has_authorization_level(auth_token, AuthorizationLevel::Basic, pool).await?;
|
|
|
|
if !has_authorization_level { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-10 05:38:32 +01:00
|
|
|
// Get a connection and open a transaction
|
2021-03-15 00:16:06 +01:00
|
|
|
let mut conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
let tx = conn.transaction().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 03:08:34 +01:00
|
|
|
// Insert the message
|
2021-03-23 05:22:54 +01:00
|
|
|
let stmt = format!("INSERT INTO {} (public_key, data, signature) VALUES (?1, ?2, ?3)", storage::MESSAGES_TABLE);
|
2021-03-22 03:25:14 +01:00
|
|
|
match tx.execute(&stmt, params![ &requesting_public_key, message.data, message.signature ]) {
|
2021-03-15 00:16:06 +01:00
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't insert message due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 05:50:24 +01:00
|
|
|
let id = tx.last_insert_rowid();
|
2021-03-10 05:13:58 +01:00
|
|
|
message.server_id = Some(id);
|
|
|
|
// Commit
|
2021-03-15 00:16:06 +01:00
|
|
|
tx.commit().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 05:13:58 +01:00
|
|
|
// Return
|
2021-03-12 05:46:06 +01:00
|
|
|
return Ok(warp::reply::json(&message).into_response());
|
2021-03-10 03:08:34 +01:00
|
|
|
}
|
|
|
|
|
2021-03-10 06:01:08 +01:00
|
|
|
/// Returns either the last `limit` messages or all messages since `from_server_id, limited to `limit`.
|
2021-03-23 23:12:54 +01:00
|
|
|
pub async fn get_messages(query_params: HashMap<String, String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-10 03:08:34 +01:00
|
|
|
// Get a database connection
|
2021-03-15 00:16:06 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-23 23:12:54 +01:00
|
|
|
// Unwrap query parameters
|
|
|
|
let from_server_id: i64;
|
|
|
|
if let Some(str) = query_params.get("from_server_id") {
|
2021-03-23 23:35:26 +01:00
|
|
|
from_server_id = str.parse().unwrap_or(0);
|
2021-03-23 23:12:54 +01:00
|
|
|
} else {
|
|
|
|
from_server_id = 0;
|
|
|
|
}
|
|
|
|
let limit: u16;
|
|
|
|
if let Some(str) = query_params.get("limit") {
|
2021-03-23 23:35:26 +01:00
|
|
|
limit = str.parse().unwrap_or(256); // Never return more than 256 messages at once
|
2021-03-23 23:12:54 +01:00
|
|
|
} else {
|
2021-03-23 23:35:26 +01:00
|
|
|
limit = 256;
|
2021-03-23 23:12:54 +01:00
|
|
|
}
|
2021-03-10 04:06:17 +01:00
|
|
|
// Query the database
|
2021-03-10 06:29:56 +01:00
|
|
|
let raw_query: String;
|
2021-03-23 23:12:54 +01:00
|
|
|
if query_params.get("from_server_id").is_some() {
|
2021-03-22 03:25:14 +01:00
|
|
|
raw_query = format!("SELECT id, data, signature FROM {} WHERE rowid > (?1) LIMIT (?2)", storage::MESSAGES_TABLE);
|
2021-03-10 04:06:17 +01:00
|
|
|
} else {
|
2021-03-22 03:25:14 +01:00
|
|
|
raw_query = format!("SELECT id, data, signature FROM {} ORDER BY rowid DESC LIMIT (?2)", storage::MESSAGES_TABLE);
|
2021-03-10 04:06:17 +01:00
|
|
|
}
|
2021-03-15 00:16:06 +01:00
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 06:29:56 +01:00
|
|
|
let rows = match query.query_map(params![ from_server_id, limit ], |row| {
|
2021-03-22 03:25:14 +01:00
|
|
|
Ok(models::Message { server_id : row.get(0)?, data : row.get(1)?, signature : row.get(2)? })
|
2021-03-10 03:08:34 +01:00
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
2021-03-15 00:16:06 +01:00
|
|
|
println!("Couldn't get messages due to error: {}.", e);
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
2021-03-10 03:08:34 +01:00
|
|
|
}
|
|
|
|
};
|
2021-03-10 23:56:32 +01:00
|
|
|
let messages: Vec<models::Message> = rows.filter_map(|result| result.ok()).collect();
|
2021-03-10 03:08:34 +01:00
|
|
|
// Return the messages
|
2021-03-19 06:44:07 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct Response {
|
|
|
|
pub messages: Vec<models::Message>
|
|
|
|
}
|
|
|
|
let response = Response { messages };
|
|
|
|
return Ok(warp::reply::json(&response).into_response());
|
2021-03-10 03:29:04 +01:00
|
|
|
}
|
|
|
|
|
2021-03-19 00:09:13 +01:00
|
|
|
// Message deletion
|
|
|
|
|
2021-03-10 03:29:04 +01:00
|
|
|
/// Deletes the message with the given `row_id` from the database, if it's present.
|
2021-03-17 23:35:51 +01:00
|
|
|
pub async fn delete_message(row_id: i64, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-17 23:58:45 +01:00
|
|
|
// Check authorization level
|
|
|
|
let (has_authorization_level, requesting_public_key) = has_authorization_level(auth_token, AuthorizationLevel::Basic, pool).await?;
|
|
|
|
if !has_authorization_level { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-18 03:21:10 +01:00
|
|
|
// Check that the requesting user is either the sender of the message or a moderator
|
2021-03-17 23:58:45 +01:00
|
|
|
let sender_option: Option<String> = {
|
2021-03-18 00:38:55 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-17 23:35:51 +01:00
|
|
|
let raw_query = format!("SELECT public_key FROM {} WHERE rowid = (?1)", storage::MESSAGES_TABLE);
|
2021-03-18 00:38:55 +01:00
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-17 23:35:51 +01:00
|
|
|
let rows = match query.query_map(params![ row_id ], |row| {
|
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't delete message due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let public_keys: Vec<String> = rows.filter_map(|result| result.ok()).collect();
|
|
|
|
public_keys.get(0).map(|s| s.to_string())
|
|
|
|
};
|
2021-03-17 23:58:45 +01:00
|
|
|
let sender = sender_option.ok_or(warp::reject::custom(Error::DatabaseFailedInternally))?;
|
|
|
|
if !is_moderator(&requesting_public_key, pool).await? && requesting_public_key != sender { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-18 00:38:55 +01:00
|
|
|
// Get a connection and open a transaction
|
|
|
|
let mut conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
let tx = conn.transaction().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 03:29:04 +01:00
|
|
|
// Delete the message if it's present
|
2021-03-10 06:29:56 +01:00
|
|
|
let stmt = format!("DELETE FROM {} WHERE rowid = (?1)", storage::MESSAGES_TABLE);
|
2021-03-15 00:16:06 +01:00
|
|
|
let count = match tx.execute(&stmt, params![ row_id ]) {
|
|
|
|
Ok(count) => count,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't delete message due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
2021-03-10 04:55:58 +01:00
|
|
|
// Update the deletions table if needed
|
2021-03-10 05:38:32 +01:00
|
|
|
if count > 0 {
|
2021-03-10 06:29:56 +01:00
|
|
|
let stmt = format!("INSERT INTO {} (id) VALUES (?1)", storage::DELETED_MESSAGES_TABLE);
|
2021-03-15 00:16:06 +01:00
|
|
|
match tx.execute(&stmt, params![ row_id ]) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't delete message due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
2021-03-10 03:29:04 +01:00
|
|
|
}
|
2021-03-10 04:55:58 +01:00
|
|
|
// Commit
|
2021-03-15 00:18:51 +01:00
|
|
|
tx.commit().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 04:55:58 +01:00
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-10 06:01:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns either the last `limit` deleted messages or all deleted messages since `from_server_id, limited to `limit`.
|
2021-03-23 23:12:54 +01:00
|
|
|
pub async fn get_deleted_messages(query_params: HashMap<String, String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-10 06:01:08 +01:00
|
|
|
// Get a database connection
|
2021-03-15 00:16:06 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-23 23:12:54 +01:00
|
|
|
// Unwrap query parameters
|
|
|
|
let from_server_id: i64;
|
|
|
|
if let Some(str) = query_params.get("from_server_id") {
|
2021-03-23 23:35:26 +01:00
|
|
|
from_server_id = str.parse().unwrap_or(0);
|
2021-03-23 23:12:54 +01:00
|
|
|
} else {
|
|
|
|
from_server_id = 0;
|
|
|
|
}
|
|
|
|
let limit: u16;
|
|
|
|
if let Some(str) = query_params.get("limit") {
|
2021-03-23 23:35:26 +01:00
|
|
|
limit = str.parse().unwrap_or(256); // Never return more than 256 messages at once
|
2021-03-23 23:12:54 +01:00
|
|
|
} else {
|
2021-03-23 23:35:26 +01:00
|
|
|
limit = 256;
|
2021-03-23 23:12:54 +01:00
|
|
|
}
|
2021-03-10 06:01:08 +01:00
|
|
|
// Query the database
|
2021-03-10 06:29:56 +01:00
|
|
|
let raw_query: String;
|
2021-03-23 23:12:54 +01:00
|
|
|
if query_params.get("from_server_id").is_some() {
|
2021-03-10 06:29:56 +01:00
|
|
|
raw_query = format!("SELECT id FROM {} WHERE rowid > (?1) LIMIT (?2)", storage::DELETED_MESSAGES_TABLE);
|
2021-03-10 06:01:08 +01:00
|
|
|
} else {
|
2021-03-10 06:29:56 +01:00
|
|
|
raw_query = format!("SELECT id FROM {} ORDER BY rowid DESC LIMIT (?2)", storage::DELETED_MESSAGES_TABLE);
|
2021-03-10 06:01:08 +01:00
|
|
|
}
|
2021-03-15 00:16:06 +01:00
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-10 06:29:56 +01:00
|
|
|
let rows = match query.query_map(params![ from_server_id, limit ], |row| {
|
2021-03-10 06:01:08 +01:00
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
2021-03-12 08:55:37 +01:00
|
|
|
println!("Couldn't query database due to error: {}.", e);
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
2021-03-10 06:01:08 +01:00
|
|
|
}
|
|
|
|
};
|
2021-03-10 23:56:32 +01:00
|
|
|
let ids: Vec<i64> = rows.filter_map(|result| result.ok()).collect();
|
2021-03-10 06:01:08 +01:00
|
|
|
// Return the IDs
|
2021-03-19 06:44:07 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct Response {
|
|
|
|
pub ids: Vec<i64>
|
|
|
|
}
|
|
|
|
let response = Response { ids };
|
|
|
|
return Ok(warp::reply::json(&response).into_response());
|
2021-03-11 00:06:09 +01:00
|
|
|
}
|
|
|
|
|
2021-03-19 00:09:13 +01:00
|
|
|
// Moderation
|
|
|
|
|
2021-03-11 00:06:09 +01:00
|
|
|
/// Returns the full list of moderators.
|
2021-03-12 05:46:06 +01:00
|
|
|
pub async fn get_moderators(pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
|
|
|
let public_keys = get_moderators_vector(pool).await?;
|
2021-03-21 23:49:54 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct Response {
|
|
|
|
pub moderators: Vec<String>
|
|
|
|
}
|
|
|
|
let response = Response { moderators : public_keys };
|
|
|
|
return Ok(warp::reply::json(&response).into_response());
|
2021-03-11 00:38:02 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 03:21:10 +01:00
|
|
|
/// Bans the given `public_key` if the requesting user is a moderator.
|
2021-03-17 23:35:51 +01:00
|
|
|
pub async fn ban(public_key: &str, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-11 00:50:17 +01:00
|
|
|
// Validate the public key
|
2021-03-11 04:20:36 +01:00
|
|
|
if !is_valid_public_key(&public_key) {
|
|
|
|
println!("Ignoring ban request for invalid public key.");
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
2021-03-11 04:20:36 +01:00
|
|
|
}
|
2021-03-17 23:58:45 +01:00
|
|
|
// Check authorization level
|
|
|
|
let (has_authorization_level, _) = has_authorization_level(auth_token, AuthorizationLevel::Moderator, pool).await?;
|
|
|
|
if !has_authorization_level { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-11 01:13:37 +01:00
|
|
|
// Don't double ban public keys
|
2021-03-12 05:46:06 +01:00
|
|
|
if is_banned(&public_key, pool).await? { return Ok(StatusCode::OK.into_response()); }
|
2021-03-19 03:26:53 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 00:50:17 +01:00
|
|
|
// Insert the message
|
|
|
|
let stmt = format!("INSERT INTO {} (public_key) VALUES (?1)", storage::BLOCK_LIST_TABLE);
|
2021-03-19 03:26:53 +01:00
|
|
|
match conn.execute(&stmt, params![ public_key ]) {
|
2021-03-15 00:16:06 +01:00
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't ban public key due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
2021-03-11 00:50:17 +01:00
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-11 00:38:02 +01:00
|
|
|
}
|
|
|
|
|
2021-03-18 03:21:10 +01:00
|
|
|
/// Unbans the given `public_key` if the requesting user is a moderator.
|
2021-03-17 23:35:51 +01:00
|
|
|
pub async fn unban(public_key: &str, auth_token: Option<String>, pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-11 00:50:17 +01:00
|
|
|
// Validate the public key
|
2021-03-11 04:20:36 +01:00
|
|
|
if !is_valid_public_key(&public_key) {
|
|
|
|
println!("Ignoring unban request for invalid public key.");
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::ValidationFailed));
|
2021-03-11 04:20:36 +01:00
|
|
|
}
|
2021-03-17 23:58:45 +01:00
|
|
|
// Check authorization level
|
|
|
|
let (has_authorization_level, _) = has_authorization_level(auth_token, AuthorizationLevel::Moderator, pool).await?;
|
|
|
|
if !has_authorization_level { return Err(warp::reject::custom(Error::Unauthorized)); }
|
2021-03-12 05:11:12 +01:00
|
|
|
// Don't double unban public keys
|
2021-03-12 05:46:06 +01:00
|
|
|
if !is_banned(&public_key, pool).await? { return Ok(StatusCode::OK.into_response()); }
|
2021-03-19 03:26:53 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 00:50:17 +01:00
|
|
|
// Insert the message
|
|
|
|
let stmt = format!("DELETE FROM {} WHERE public_key = (?1)", storage::BLOCK_LIST_TABLE);
|
2021-03-19 03:26:53 +01:00
|
|
|
match conn.execute(&stmt, params![ public_key ]) {
|
2021-03-15 00:16:06 +01:00
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't unban public key due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
2021-03-11 00:50:17 +01:00
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
let json = models::StatusCode { status_code : StatusCode::OK.as_u16() };
|
|
|
|
return Ok(warp::reply::json(&json).into_response());
|
2021-03-11 00:38:02 +01:00
|
|
|
}
|
|
|
|
|
2021-03-11 01:02:28 +01:00
|
|
|
/// Returns the full list of banned public keys.
|
2021-03-12 05:46:06 +01:00
|
|
|
pub async fn get_banned_public_keys(pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
|
|
|
let public_keys = get_banned_public_keys_vector(pool).await?;
|
2021-03-19 06:44:07 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct Response {
|
|
|
|
pub banned_members: Vec<String>
|
|
|
|
}
|
|
|
|
let response = Response { banned_members : public_keys };
|
|
|
|
return Ok(warp::reply::json(&response).into_response());
|
2021-03-11 01:02:28 +01:00
|
|
|
}
|
|
|
|
|
2021-03-19 00:09:13 +01:00
|
|
|
// General
|
|
|
|
|
2021-03-12 05:46:06 +01:00
|
|
|
pub async fn get_member_count(pool: &storage::DatabaseConnectionPool) -> Result<Response, Rejection> {
|
2021-03-18 00:23:43 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
// Query the database
|
|
|
|
let raw_query = format!("SELECT public_key FROM {}", storage::TOKENS_TABLE);
|
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
let rows = match query.query_map(params![], |row| {
|
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't query database due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let public_keys: Vec<String> = rows.filter_map(|result| result.ok()).collect();
|
|
|
|
let public_key_count = public_keys.len();
|
2021-03-18 05:35:18 +01:00
|
|
|
// Return
|
2021-03-19 06:44:07 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct Response {
|
|
|
|
pub member_count: usize
|
|
|
|
}
|
|
|
|
let response = Response { member_count : public_key_count };
|
|
|
|
return Ok(warp::reply::json(&response).into_response());
|
2021-03-11 01:10:49 +01:00
|
|
|
}
|
|
|
|
|
2021-03-11 00:38:02 +01:00
|
|
|
// Utilities
|
|
|
|
|
2021-03-17 23:35:51 +01:00
|
|
|
async fn get_moderators_vector(pool: &storage::DatabaseConnectionPool) -> Result<Vec<String>, Rejection> {
|
2021-03-11 00:06:09 +01:00
|
|
|
// Get a database connection
|
2021-03-15 00:16:06 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 00:06:09 +01:00
|
|
|
// Query the database
|
|
|
|
let raw_query = format!("SELECT public_key FROM {}", storage::MODERATORS_TABLE);
|
2021-03-15 00:16:06 +01:00
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 00:06:09 +01:00
|
|
|
let rows = match query.query_map(params![], |row| {
|
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
2021-03-12 08:55:37 +01:00
|
|
|
println!("Couldn't query database due to error: {}.", e);
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
2021-03-11 00:06:09 +01:00
|
|
|
}
|
|
|
|
};
|
2021-03-11 00:38:02 +01:00
|
|
|
// Return
|
|
|
|
return Ok(rows.filter_map(|result| result.ok()).collect());
|
|
|
|
}
|
|
|
|
|
2021-03-17 23:35:51 +01:00
|
|
|
async fn is_moderator(public_key: &str, pool: &storage::DatabaseConnectionPool) -> Result<bool, Rejection> {
|
2021-03-12 05:46:06 +01:00
|
|
|
let public_keys = get_moderators_vector(&pool).await?;
|
2021-03-11 00:38:02 +01:00
|
|
|
return Ok(public_keys.contains(&public_key.to_owned()));
|
2021-03-11 00:50:17 +01:00
|
|
|
}
|
|
|
|
|
2021-03-17 23:35:51 +01:00
|
|
|
async fn get_banned_public_keys_vector(pool: &storage::DatabaseConnectionPool) -> Result<Vec<String>, Rejection> {
|
2021-03-11 01:02:28 +01:00
|
|
|
// Get a database connection
|
2021-03-15 00:16:06 +01:00
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 01:02:28 +01:00
|
|
|
// Query the database
|
|
|
|
let raw_query = format!("SELECT public_key FROM {}", storage::BLOCK_LIST_TABLE);
|
2021-03-15 00:16:06 +01:00
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-11 01:02:28 +01:00
|
|
|
let rows = match query.query_map(params![], |row| {
|
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
2021-03-12 08:55:37 +01:00
|
|
|
println!("Couldn't query database due to error: {}.", e);
|
2021-03-12 06:40:24 +01:00
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
2021-03-11 01:02:28 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
// Return
|
|
|
|
return Ok(rows.filter_map(|result| result.ok()).collect());
|
|
|
|
}
|
|
|
|
|
2021-03-17 23:35:51 +01:00
|
|
|
async fn is_banned(public_key: &str, pool: &storage::DatabaseConnectionPool) -> Result<bool, Rejection> {
|
2021-03-12 05:46:06 +01:00
|
|
|
let public_keys = get_banned_public_keys_vector(&pool).await?;
|
2021-03-11 01:02:28 +01:00
|
|
|
return Ok(public_keys.contains(&public_key.to_owned()));
|
|
|
|
}
|
|
|
|
|
2021-03-17 23:35:51 +01:00
|
|
|
fn is_valid_public_key(public_key: &str) -> bool {
|
2021-03-11 01:02:28 +01:00
|
|
|
// Check that it's a valid hex encoding
|
2021-03-16 04:33:55 +01:00
|
|
|
if hex::decode(public_key).is_err() { return false; }
|
2021-03-11 01:02:28 +01:00
|
|
|
// Check that it's the right length
|
|
|
|
if public_key.len() != 66 { return false } // The version byte + 32 bytes of random data
|
|
|
|
// It appears to be a valid public key
|
|
|
|
return true
|
2021-03-17 23:35:51 +01:00
|
|
|
}
|
|
|
|
|
2021-03-17 23:58:45 +01:00
|
|
|
async fn get_public_key_for_auth_token(auth_token: &str, pool: &storage::DatabaseConnectionPool) -> Result<Option<String>, Rejection> {
|
2021-03-17 23:35:51 +01:00
|
|
|
// Get a database connection
|
|
|
|
let conn = pool.get().map_err(|_| Error::DatabaseFailedInternally)?;
|
|
|
|
// Query the database
|
|
|
|
let raw_query = format!("SELECT public_key FROM {} WHERE token = (?1)", storage::TOKENS_TABLE);
|
|
|
|
let mut query = conn.prepare(&raw_query).map_err(|_| Error::DatabaseFailedInternally)?;
|
2021-03-17 23:58:45 +01:00
|
|
|
let rows = match query.query_map(params![ auth_token ], |row| {
|
2021-03-17 23:35:51 +01:00
|
|
|
Ok(row.get(0)?)
|
|
|
|
}) {
|
|
|
|
Ok(rows) => rows,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Couldn't query database due to error: {}.", e);
|
|
|
|
return Err(warp::reject::custom(Error::DatabaseFailedInternally));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let public_keys: Vec<String> = rows.filter_map(|result| result.ok()).collect();
|
|
|
|
// Return
|
|
|
|
return Ok(public_keys.get(0).map(|s| s.to_string()));
|
2021-03-17 23:58:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn has_authorization_level(auth_token: Option<String>, level: AuthorizationLevel, pool: &storage::DatabaseConnectionPool) -> Result<(bool, String), Rejection> {
|
|
|
|
// Check that the auth token is present
|
|
|
|
let auth_token = auth_token.ok_or(warp::reject::custom(Error::Unauthorized))?;
|
|
|
|
// Check that we have a public key associated with the given auth token
|
|
|
|
let public_key_option = get_public_key_for_auth_token(&auth_token, pool).await?;
|
|
|
|
let public_key = public_key_option.ok_or(warp::reject::custom(Error::Unauthorized))?;
|
|
|
|
// Check that the given public key isn't banned
|
|
|
|
if is_banned(&public_key, pool).await? { return Err(warp::reject::custom(Error::Unauthorized)); }
|
|
|
|
// If needed, check that the given public key is a moderator
|
|
|
|
match level {
|
2021-03-18 01:52:39 +01:00
|
|
|
AuthorizationLevel::Basic => return Ok((true, public_key)),
|
|
|
|
AuthorizationLevel::Moderator => {
|
2021-03-17 23:58:45 +01:00
|
|
|
if !is_moderator(&public_key, pool).await? { return Err(warp::reject::custom(Error::Unauthorized)); }
|
|
|
|
return Ok((true, public_key));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|