2021-03-25 00:56:16 +01:00
use std ::collections ::HashMap ;
2021-03-22 03:55:43 +01:00
use std ::convert ::TryInto ;
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-04-01 00:55:47 +02:00
use log ::{ error , info , warn } ;
2021-03-16 06:25:59 +01:00
use rand ::{ thread_rng , Rng } ;
2021-03-25 00:56:16 +01:00
use rusqlite ::params ;
use serde ::{ Deserialize , Serialize } ;
2021-04-01 01:32:25 +02:00
use tokio ::fs ::File ;
use tokio ::io ::{ AsyncReadExt , AsyncWriteExt } ;
2021-03-25 00:56:16 +01:00
use warp ::{ http ::StatusCode , reply ::Reply , reply ::Response , Rejection } ;
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 {
2021-03-25 00:56:16 +01:00
Basic ,
2021-03-25 01:38:06 +01:00
Moderator ,
2021-03-17 23:58:45 +01:00
}
2021-03-22 03:55:43 +01:00
#[ derive(Debug, Deserialize, Serialize) ]
2021-03-25 00:56:16 +01:00
pub struct GenericStringResponse {
2021-03-24 00:02:53 +01:00
pub status_code : u16 ,
2021-03-25 01:38:06 +01:00
pub result : String ,
2021-03-22 03:55:43 +01:00
}
2021-03-23 05:22:54 +01:00
// Rooms
2021-03-31 02:00:02 +02:00
// Not publicly exposed.
pub async fn create_room ( room : models ::Room ) -> Result < Response , Rejection > {
2021-03-23 05:22:54 +01:00
// 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-31 02:00:02 +02:00
match conn . execute ( & stmt , params! [ & room . id , & room . name ] ) {
2021-03-23 05:22:54 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't create room due to error: {}. " , e ) ;
2021-03-23 05:22:54 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
}
2021-03-23 05:27:47 +01:00
// Set up the database
2021-03-31 02:00:02 +02:00
storage ::create_database_if_needed ( & room . id ) ;
2021-03-23 05:22:54 +01:00
// Return
2021-04-01 00:55:47 +02:00
info! ( " Added room with ID: {} " , & room . id ) ;
2021-03-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-23 05:22:54 +01:00
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
}
2021-03-31 02:00:02 +02:00
// Not publicly exposed.
pub async fn delete_room ( id : String ) -> Result < Response , Rejection > {
2021-03-30 05:01:59 +02:00
// Get a connection
let pool = & storage ::MAIN_POOL ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Insert the room
let stmt = format! ( " DELETE FROM {} WHERE id = (?1) " , storage ::MAIN_TABLE ) ;
2021-03-31 02:00:02 +02:00
match conn . execute ( & stmt , params! [ & id ] ) {
2021-03-30 05:01:59 +02:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete room due to error: {}. " , e ) ;
2021-03-30 05:01:59 +02:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
}
2021-04-01 00:55:47 +02:00
// Don't auto-delete the database file (the server operator might want to keep it around)
2021-03-30 05:01:59 +02:00
// Return
2021-04-01 00:55:47 +02:00
info! ( " Deleted room with ID: {} " , & id ) ;
2021-03-30 05:01:59 +02:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
}
2021-03-31 02:23:45 +02:00
pub fn get_room ( room_id : & str ) -> Result < Response , Rejection > {
2021-03-25 04:05:46 +01:00
// Get a connection
let pool = & storage ::MAIN_POOL ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Get the room info if possible
2021-04-01 00:55:47 +02:00
let raw_query = format! ( " SELECT id, name FROM {} where id = (?1) " , storage ::MAIN_TABLE ) ;
2021-03-25 04:11:45 +01:00
let room = match conn . query_row ( & raw_query , params! [ room_id ] , | row | {
2021-04-01 00:55:47 +02:00
Ok ( models ::Room { id : row . get ( 0 ) ? , name : row . get ( 1 ) ? } )
2021-03-25 04:11:45 +01:00
} ) {
2021-03-25 04:05:46 +01:00
Ok ( info ) = > info ,
Err ( _ ) = > return Err ( warp ::reject ::custom ( Error ::NoSuchRoom ) ) ,
} ;
// Return
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
2021-03-31 02:00:02 +02:00
room : models ::Room ,
2021-03-25 04:05:46 +01:00
}
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , room } ;
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
}
2021-03-31 02:23:45 +02:00
pub fn get_all_rooms ( ) -> Result < Response , Rejection > {
2021-03-25 04:05:46 +01:00
// Get a connection
let pool = & storage ::MAIN_POOL ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Get the room info if possible
2021-04-01 00:55:47 +02:00
let raw_query = format! ( " SELECT id, name FROM {} " , storage ::MAIN_TABLE ) ;
2021-03-25 04:05:46 +01:00
let mut query = conn . prepare ( & raw_query ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
2021-04-01 00:55:47 +02:00
let rows = match query
. query_map ( params! [ ] , | row | Ok ( models ::Room { id : row . get ( 0 ) ? , name : row . get ( 1 ) ? } ) )
{
2021-03-25 04:05:46 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't get rooms due to error: {}. " , e ) ;
2021-03-25 04:05:46 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
2021-03-31 02:00:02 +02:00
let rooms : Vec < models ::Room > = rows . filter_map ( | result | result . ok ( ) ) . collect ( ) ;
2021-03-25 04:05:46 +01:00
// Return
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
2021-03-31 02:00:02 +02:00
rooms : Vec < models ::Room > ,
2021-03-25 04:05:46 +01:00
}
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , rooms } ;
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
}
2021-03-19 00:14:14 +01:00
// Files
2021-04-01 01:32:25 +02:00
pub async fn store_file (
2021-03-25 01:38:06 +01:00
base64_encoded_bytes : & str , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-26 05:24:02 +01:00
// It'd be nice to use the UUID crate for the file ID, but clients want an integer ID
2021-04-09 03:35:09 +02:00
let now = chrono ::Utc ::now ( ) . timestamp_nanos ( ) ;
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-19 00:09:13 +01:00
// Parse bytes
let bytes = match base64 ::decode ( base64_encoded_bytes ) {
Ok ( bytes ) = > bytes ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't parse bytes from invalid base64 encoding due to error: {}. " , e ) ;
2021-03-19 00:09:13 +01:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
}
} ;
// 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.
2021-03-19 03:26:53 +01:00
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
2021-04-01 00:55:47 +02:00
// INSERT rather than REPLACE so that on the off chance there's already a file with this exact
// id (i.e. timestamp) we simply error out and get the client to retry.
2021-03-19 00:09:13 +01:00
let stmt = format! ( " INSERT INTO {} (id, timestamp) VALUES (?1, ?2) " , storage ::FILES_TABLE ) ;
2021-03-26 05:24:02 +01:00
let _ = match conn . execute ( & stmt , params! [ now , now ] ) {
2021-03-19 00:09:13 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't insert file record due to error: {}. " , e ) ;
2021-03-19 00:09:13 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
// Write to file
2021-03-26 05:24:02 +01:00
let raw_path = format! ( " files/ {} " , & now ) ;
2021-03-23 04:25:52 +01:00
let path = Path ::new ( & raw_path ) ;
2021-04-01 01:32:25 +02:00
let mut file = match File ::create ( path ) . await {
Ok ( file ) = > file ,
Err ( e ) = > {
error! ( " Couldn't store file due to error: {}. " , e ) ;
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
match file . write_all ( & bytes ) . await {
Ok ( _ ) = > ( ) ,
2021-03-19 00:09:13 +01:00
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't store file due to error: {}. " , e ) ;
2021-03-19 00:09:13 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
// Return
2021-03-26 05:24:02 +01:00
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
result : i64 ,
}
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , result : now } ;
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
2021-03-19 00:09:13 +01:00
}
2021-04-01 01:32:25 +02:00
pub async fn get_file (
2021-03-26 05:24:02 +01:00
id : i64 , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < GenericStringResponse , Rejection > {
// Doesn't return a response directly for testing purposes
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-19 00:09:13 +01:00
// Try to read the file
2021-04-01 01:32:25 +02:00
let mut bytes = vec! [ ] ;
2021-03-23 04:25:52 +01:00
let raw_path = format! ( " files/ {} " , id ) ;
let path = Path ::new ( & raw_path ) ;
2021-04-01 01:32:25 +02:00
let mut file = match File ::open ( path ) . await {
Ok ( file ) = > file ,
Err ( e ) = > {
error! ( " Couldn't read file due to error: {}. " , e ) ;
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
}
} ;
match file . read_to_end ( & mut bytes ) . await {
Ok ( _ ) = > ( ) ,
2021-03-19 00:09:13 +01:00
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't read file due to error: {}. " , e ) ;
2021-03-19 00:09:13 +01:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
}
} ;
// Base64 encode the result
let base64_encoded_bytes = base64 ::encode ( bytes ) ;
// Return
2021-03-25 00:56:16 +01:00
let json = GenericStringResponse {
status_code : StatusCode ::OK . as_u16 ( ) ,
2021-03-25 01:38:06 +01:00
result : base64_encoded_bytes ,
2021-03-25 00:56:16 +01:00
} ;
2021-03-19 06:44:07 +01:00
return Ok ( json ) ;
2021-03-19 00:09:13 +01:00
}
2021-04-01 01:32:25 +02:00
pub async fn get_group_image ( room_id : & str ) -> Result < Response , Rejection > {
2021-03-29 07:11:56 +02:00
// Try to read the file
2021-04-01 01:32:25 +02:00
let mut bytes = vec! [ ] ;
2021-03-29 07:11:56 +02:00
let raw_path = format! ( " files/ {} " , room_id ) ;
let path = Path ::new ( & raw_path ) ;
2021-04-01 01:32:25 +02:00
let mut file = match File ::open ( path ) . await {
Ok ( file ) = > file ,
Err ( e ) = > {
error! ( " Couldn't read file due to error: {}. " , e ) ;
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
}
} ;
match file . read_to_end ( & mut bytes ) . await {
Ok ( _ ) = > ( ) ,
2021-03-29 07:11:56 +02:00
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't read file due to error: {}. " , e ) ;
2021-03-29 07:11:56 +02:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
}
} ;
// Base64 encode the result
let base64_encoded_bytes = base64 ::encode ( bytes ) ;
// Return
let json = GenericStringResponse {
status_code : StatusCode ::OK . as_u16 ( ) ,
result : base64_encoded_bytes ,
} ;
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
}
2021-03-19 00:09:13 +01:00
// Authentication
2021-03-31 02:23:45 +02:00
pub fn get_auth_token_challenge (
2021-03-25 01:38:06 +01:00
query_params : HashMap < String , String > , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < models ::Challenge , Rejection > {
// Doesn't return a response directly for testing purposes
2021-03-23 23:12:54 +01:00
// Get the public key
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
if ! is_valid_public_key ( hex_public_key ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring challenge request for invalid public key: {}. " , hex_public_key ) ;
2021-03-25 00:56:16 +01:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
2021-03-16 06:25:59 +01:00
}
// Convert the public key to bytes and cut off the version byte
2021-03-29 06:53:57 +02:00
// This is safe because we know it has a length of 32 at this point
let public_key : [ u8 ; 32 ] = hex ::decode ( hex_public_key ) . unwrap ( ) [ 1 .. ] . try_into ( ) . unwrap ( ) ;
// Generate an ephemeral key pair
2021-03-31 02:23:45 +02:00
let ( ephemeral_private_key , ephemeral_public_key ) = crypto ::generate_x25519_key_pair ( ) ;
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-31 02:23:45 +02:00
let symmetric_key = crypto ::get_x25519_symmetric_key ( & public_key , & ephemeral_private_key ) ? ;
2021-03-29 06:53:57 +02:00
// Generate a random token (or get the currently pending one if possible)
2021-03-31 02:23:45 +02:00
let pending_tokens = get_pending_tokens ( & hex_public_key , & pool ) ? ;
2021-03-29 06:53:57 +02:00
let token : Vec < u8 > ;
if ! pending_tokens . is_empty ( ) {
token = pending_tokens [ 0 ] . 1. clone ( ) ;
} else {
let mut buffer = [ 0 u8 ; 48 ] ;
thread_rng ( ) . fill ( & mut buffer [ .. ] ) ;
token = buffer . to_vec ( ) ;
}
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 conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
2021-03-29 06:53:57 +02:00
let now = chrono ::Utc ::now ( ) . timestamp ( ) ;
2021-03-25 00:56:16 +01:00
let stmt = format! (
" INSERT INTO {} (public_key, timestamp, token) VALUES (?1, ?2, ?3) " ,
storage ::PENDING_TOKENS_TABLE
) ;
2021-03-29 06:53:57 +02:00
let _ = match conn . execute ( & stmt , params! [ hex_public_key , now , token ] ) {
2021-03-19 03:26:53 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't insert pending token due to error: {}. " , e ) ;
2021-03-19 03:26:53 +01:00
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
2021-03-31 02:23:45 +02:00
let ciphertext = crypto ::encrypt_aes_gcm ( & token , & symmetric_key ) ? ;
2021-03-16 06:25:59 +01:00
// Return
2021-03-25 00:56:16 +01:00
return Ok ( models ::Challenge {
ciphertext : base64 ::encode ( ciphertext ) ,
2021-03-25 01:38:06 +01:00
ephemeral_public_key : base64 ::encode ( ephemeral_public_key . to_bytes ( ) ) ,
2021-03-25 00:56:16 +01:00
} ) ;
2021-03-16 06:25:59 +01:00
}
2021-03-31 02:23:45 +02:00
pub fn claim_auth_token (
2021-03-25 01:38:06 +01:00
public_key : & str , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-17 00:10:26 +01:00
// Validate the public key
2021-03-25 00:56:16 +01:00
if ! is_valid_public_key ( & public_key ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring claim token request for invalid public key. " ) ;
2021-03-25 00:56:16 +01:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
2021-03-17 00:10:26 +01:00
}
// Validate the token
2021-03-25 00:56:16 +01:00
if hex ::decode ( auth_token ) . is_err ( ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring claim token request for invalid token. " ) ;
2021-03-25 00:56:16 +01:00
return Err ( warp ::reject ::custom ( Error ::ValidationFailed ) ) ;
2021-03-17 00:10:26 +01:00
}
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-31 02:23:45 +02:00
let pending_tokens = get_pending_tokens ( & public_key , & pool ) ? ;
2021-03-17 00:10:26 +01:00
// Check that the token being claimed is in fact one of the pending tokens
2021-03-25 00:17:47 +01:00
let claim = hex ::decode ( auth_token ) . unwrap ( ) ; // Safe because we validated it above
2021-03-25 00:56:16 +01:00
let index = pending_tokens
. iter ( )
. position ( | ( _ , pending_token ) | * pending_token = = claim )
. ok_or_else ( | | Error ::Unauthorized ) ? ;
2021-03-17 00:40:50 +01:00
let token = & pending_tokens [ index ] . 1 ;
2021-03-17 00:10:26 +01:00
// Store the claimed token
2021-03-25 00:56:16 +01:00
let stmt = format! (
" INSERT OR REPLACE INTO {} (public_key, token) VALUES (?1, ?2) " ,
storage ::TOKENS_TABLE
) ;
match conn . execute ( & stmt , params! [ public_key , hex ::encode ( token ) ] ) {
2021-03-17 00:10:26 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't insert token due to error: {}. " , e ) ;
2021-03-17 00:10:26 +01:00
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 ) ;
2021-03-25 00:56:16 +01:00
match conn . execute ( & stmt , params! [ public_key ] ) {
2021-03-19 03:26:53 +01:00
Ok ( _ ) = > ( ) ,
2021-04-01 00:55:47 +02:00
Err ( e ) = > error! ( " Couldn't delete pending tokens due to error: {}. " , e ) , // It's not catastrophic if this fails
2021-03-19 03:26:53 +01:00
} ;
2021-03-17 00:10:26 +01:00
// Return
2021-03-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-19 06:44:07 +01:00
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
2021-03-16 06:40:51 +01:00
}
2021-03-31 02:23:45 +02:00
pub fn delete_auth_token (
2021-03-25 01:38:06 +01:00
auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-18 00:23:43 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , requesting_public_key ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +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 ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete token due to error: {}. " , e ) ;
2021-03-18 00:23:43 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
// Return
2021-03-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-19 06:44:07 +01:00
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-31 02:23:45 +02:00
pub fn insert_message (
2021-03-25 01:38:06 +01:00
mut message : models ::Message , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-10 03:08:34 +01:00
// Validate the message
2021-03-25 00:56:16 +01:00
if ! message . is_valid ( ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring invalid message. " ) ;
2021-03-25 00:56:16 +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
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , requesting_public_key ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
let stmt = format! (
2021-03-26 00:21:08 +01:00
" INSERT INTO {} (public_key, timestamp, data, signature) VALUES (?1, ?2, ?3, ?4) " ,
2021-03-25 00:56:16 +01:00
storage ::MESSAGES_TABLE
) ;
2021-03-26 00:21:08 +01:00
match tx . execute (
& stmt ,
params! [ & requesting_public_key , message . timestamp , message . data , message . signature ] ,
) {
2021-03-15 00:16:06 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't insert message due to error: {}. " , e ) ;
2021-03-15 00:16:06 +01:00
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 ) ;
2021-03-24 04:11:06 +01:00
message . public_key = Some ( requesting_public_key ) ;
2021-03-10 05:13:58 +01:00
// 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-24 00:02:53 +01:00
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
2021-03-25 01:38:06 +01:00
message : models ::Message ,
2021-03-24 00:02:53 +01:00
}
2021-03-25 00:56:16 +01:00
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , message } ;
2021-03-24 00:02:53 +01:00
return Ok ( warp ::reply ::json ( & response ) . 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-31 02:23:45 +02:00
pub fn get_messages (
2021-03-25 01:38:06 +01:00
query_params : HashMap < String , String > , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-04-16 07:02:43 +02:00
) -> Result < Vec < models ::Message > , Rejection > {
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
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 ;
}
2021-03-24 23:33:47 +01:00
let limit : u16 ; // Never return more than 256 messages at once
2021-03-23 23:12:54 +01:00
if let Some ( str ) = query_params . get ( " limit " ) {
2021-03-24 23:33:47 +01:00
limit = std ::cmp ::min ( str . parse ( ) . unwrap_or ( 256 ) , 256 ) ;
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-26 00:21:08 +01:00
raw_query = format! ( " SELECT id, public_key, timestamp, data, signature FROM {} WHERE rowid > (?1) ORDER BY rowid ASC LIMIT (?2) " , storage ::MESSAGES_TABLE ) ;
2021-03-10 04:06:17 +01:00
} else {
2021-03-25 00:56:16 +01:00
raw_query = format! (
2021-03-26 00:21:08 +01:00
" SELECT id, public_key, timestamp, data, signature FROM {} ORDER BY rowid DESC LIMIT (?2) " ,
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ from_server_id , limit ] , | row | {
Ok ( models ::Message {
server_id : row . get ( 0 ) ? ,
public_key : row . get ( 1 ) ? ,
2021-03-26 00:21:08 +01:00
timestamp : row . get ( 2 ) ? ,
data : row . get ( 3 ) ? ,
signature : row . get ( 4 ) ? ,
2021-03-25 00:56:16 +01:00
} )
2021-03-10 03:08:34 +01:00
} ) {
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " 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-04-16 07:02:43 +02:00
return Ok ( messages ) ;
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-31 02:23:45 +02:00
pub fn delete_message (
2021-03-25 01:38:06 +01:00
row_id : i64 , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-17 23:58:45 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , requesting_public_key ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ row_id ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-17 23:35:51 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete message due to error: {}. " , e ) ;
2021-03-17 23:35:51 +01:00
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 ) ) ? ;
2021-03-31 02:23:45 +02:00
if ! is_moderator ( & requesting_public_key , pool ) ? & & requesting_public_key ! = sender {
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
let count = match tx . execute ( & stmt , params! [ row_id ] ) {
2021-03-15 00:16:06 +01:00
Ok ( count ) = > count ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete message due to error: {}. " , e ) ;
2021-03-15 00:16:06 +01:00
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-25 00:56:16 +01:00
match tx . execute ( & stmt , params! [ row_id ] ) {
2021-03-15 00:16:06 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete message due to error: {}. " , e ) ;
2021-03-15 00:16:06 +01:00
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-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-19 06:44:07 +01:00
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-31 02:23:45 +02:00
pub fn get_deleted_messages (
2021-03-25 01:38:06 +01:00
query_params : HashMap < String , String > , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-04-16 07:02:43 +02:00
) -> Result < Vec < i64 > , Rejection > {
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
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 ;
}
2021-03-24 23:33:47 +01:00
let limit : u16 ; // Never return more than 256 messages at once
2021-03-23 23:12:54 +01:00
if let Some ( str ) = query_params . get ( " limit " ) {
2021-03-24 23:33:47 +01:00
limit = std ::cmp ::min ( str . parse ( ) . unwrap_or ( 256 ) , 256 ) ;
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-25 00:56:16 +01:00
raw_query = format! (
" SELECT id FROM {} WHERE rowid > (?1) ORDER BY rowid ASC LIMIT (?2) " ,
storage ::DELETED_MESSAGES_TABLE
) ;
2021-03-10 06:01:08 +01:00
} else {
2021-03-25 00:56:16 +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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ from_server_id , limit ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-10 06:01:08 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " 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-04-16 07:02:43 +02:00
return Ok ( ids ) ;
2021-03-11 00:06:09 +01:00
}
2021-03-19 00:09:13 +01:00
// Moderation
2021-03-31 02:46:54 +02:00
// Not publicly exposed.
pub async fn add_moderator (
body : models ::ChangeModeratorRequestBody ,
2021-03-26 06:10:16 +01:00
) -> Result < Response , Rejection > {
// Get a database connection
2021-03-31 02:00:02 +02:00
let pool = storage ::pool_by_room_id ( & body . room_id ) ;
2021-03-26 06:10:16 +01:00
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Insert the moderator
let stmt = format! ( " INSERT INTO {} (public_key) VALUES (?1) " , storage ::MODERATORS_TABLE ) ;
2021-03-31 02:00:02 +02:00
match conn . execute ( & stmt , params! [ & body . public_key ] ) {
2021-03-26 06:10:16 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't make public key moderator due to error: {}. " , e ) ;
2021-03-26 06:10:16 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
}
// Return
2021-04-01 00:55:47 +02:00
info! ( " Added moderator: {} to room with ID: {} " , & body . public_key , & body . room_id ) ;
2021-03-26 06:10:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
}
2021-03-31 06:05:23 +02:00
// Not publicly exposed.
2021-03-31 02:46:54 +02:00
pub async fn delete_moderator (
body : models ::ChangeModeratorRequestBody ,
) -> Result < Response , Rejection > {
// Get a database connection
let pool = storage ::pool_by_room_id ( & body . room_id ) ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Insert the moderator
let stmt = format! ( " DELETE FROM {} WHERE public_key = (?1) " , storage ::MODERATORS_TABLE ) ;
match conn . execute ( & stmt , params! [ & body . public_key ] ) {
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't delete moderator due to error: {}. " , e ) ;
2021-03-31 02:46:54 +02:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
}
// Return
2021-04-01 00:55:47 +02:00
info! ( " Deleted moderator: {} from room with ID: {} " , & body . public_key , & body . room_id ) ;
2021-03-31 02:46:54 +02:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
return Ok ( warp ::reply ::json ( & json ) . into_response ( ) ) ;
}
2021-03-11 00:06:09 +01:00
/// Returns the full list of moderators.
2021-03-31 02:23:45 +02:00
pub fn get_moderators (
2021-03-25 01:38:06 +01:00
auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-04-16 07:02:43 +02:00
) -> Result < Vec < String > , Rejection > {
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-25 00:17:47 +01:00
// Return
2021-03-31 02:23:45 +02:00
let public_keys = get_moderators_vector ( pool ) ? ;
2021-04-16 07:02:43 +02:00
return Ok ( public_keys ) ;
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-31 02:23:45 +02:00
pub fn ban (
2021-03-25 01:38:06 +01:00
public_key : & str , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-11 00:50:17 +01:00
// Validate the public key
2021-03-25 00:56:16 +01:00
if ! is_valid_public_key ( & public_key ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring ban request for invalid public key. " ) ;
2021-03-25 00:56:16 +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
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Moderator , pool ) ? ;
2021-03-25 00:56:16 +01:00
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-31 02:23:45 +02:00
if is_banned ( & public_key , pool ) ? {
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
match conn . execute ( & stmt , params! [ public_key ] ) {
2021-03-15 00:16:06 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't ban public key due to error: {}. " , e ) ;
2021-03-15 00:16:06 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
2021-03-11 00:50:17 +01:00
// Return
2021-03-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-19 06:44:07 +01:00
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-31 02:23:45 +02:00
pub fn unban (
2021-03-25 01:38:06 +01:00
public_key : & str , auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-11 00:50:17 +01:00
// Validate the public key
2021-03-25 00:56:16 +01:00
if ! is_valid_public_key ( & public_key ) {
2021-04-01 00:55:47 +02:00
warn! ( " Ignoring unban request for invalid public key. " ) ;
2021-03-25 00:56:16 +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
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Moderator , pool ) ? ;
2021-03-25 00:56:16 +01:00
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-31 02:23:45 +02:00
if ! is_banned ( & public_key , pool ) ? {
2021-03-25 00:56:16 +01:00
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-25 00:56:16 +01:00
match conn . execute ( & stmt , params! [ public_key ] ) {
2021-03-15 00:16:06 +01:00
Ok ( _ ) = > ( ) ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't unban public key due to error: {}. " , e ) ;
2021-03-15 00:16:06 +01:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
2021-03-11 00:50:17 +01:00
// Return
2021-03-25 00:56:16 +01:00
let json = models ::StatusCode { status_code : StatusCode ::OK . as_u16 ( ) } ;
2021-03-19 06:44:07 +01:00
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-31 02:23:45 +02:00
pub fn get_banned_public_keys (
2021-03-25 01:38:06 +01:00
auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-25 00:17:47 +01:00
// Return
2021-03-31 02:23:45 +02:00
let public_keys = get_banned_public_keys_vector ( pool ) ? ;
2021-03-19 06:44:07 +01:00
#[ derive(Debug, Deserialize, Serialize) ]
2021-03-25 00:56:16 +01:00
struct Response {
2021-03-24 00:02:53 +01:00
status_code : u16 ,
2021-03-25 01:38:06 +01:00
banned_members : Vec < String > ,
2021-03-19 06:44:07 +01:00
}
2021-03-25 00:56:16 +01:00
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , banned_members : public_keys } ;
2021-03-19 06:44:07 +01:00
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-31 02:23:45 +02:00
pub fn get_member_count (
2021-03-25 01:38:06 +01:00
auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < Response , Rejection > {
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
2021-03-31 02:23:45 +02:00
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) ? ;
2021-03-25 00:56:16 +01:00
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
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 ) ? ;
2021-03-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-18 00:23:43 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't query database due to error: {}. " , e ) ;
2021-03-18 00:23:43 +01:00
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) ]
2021-03-25 00:56:16 +01:00
struct Response {
2021-03-24 00:02:53 +01:00
status_code : u16 ,
2021-03-25 01:38:06 +01:00
member_count : usize ,
2021-03-19 06:44:07 +01:00
}
2021-03-25 00:56:16 +01:00
let response =
Response { status_code : StatusCode ::OK . as_u16 ( ) , member_count : public_key_count } ;
2021-03-19 06:44:07 +01:00
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
2021-03-11 01:10:49 +01:00
}
2021-04-16 07:02:43 +02:00
pub fn compact_poll (
request_bodies : Vec < models ::CompactPollRequestBody > ,
) -> Result < Response , Rejection > {
let mut response_bodies : Vec < models ::CompactPollResponseBody > = vec! [ ] ;
for request_body in request_bodies {
// Unwrap the request body
2021-04-19 03:00:03 +02:00
let models ::CompactPollRequestBody {
room_id ,
auth_token ,
from_message_server_id ,
from_deletion_server_id ,
} = request_body ;
2021-04-16 07:02:43 +02:00
// Get the database connection pool
let pool = storage ::pool_by_room_id ( & room_id ) ;
// Get the new messages
let mut get_messages_query_params : HashMap < String , String > = HashMap ::new ( ) ;
2021-04-19 03:00:03 +02:00
if let Some ( from_message_server_id ) = from_message_server_id {
get_messages_query_params
. insert ( " from_server_id " . to_string ( ) , from_message_server_id . to_string ( ) ) ;
}
2021-04-22 07:41:29 +02:00
let messages = match get_messages ( get_messages_query_params , & auth_token , & pool ) {
Ok ( messages ) = > messages ,
Err ( e ) = > {
let status_code = super ::errors ::status_code ( e ) ;
let response_body = models ::CompactPollResponseBody {
room_id ,
status_code : status_code . as_u16 ( ) ,
messages : vec ! [ ] ,
deletions : vec ! [ ] ,
moderators : vec ! [ ] ,
} ;
response_bodies . push ( response_body ) ;
continue ;
}
} ;
2021-04-16 07:02:43 +02:00
// Get the new deletions
let mut get_deletions_query_params : HashMap < String , String > = HashMap ::new ( ) ;
2021-04-19 03:00:03 +02:00
if let Some ( from_deletion_server_id ) = from_deletion_server_id {
get_deletions_query_params
. insert ( " from_server_id " . to_string ( ) , from_deletion_server_id . to_string ( ) ) ;
}
2021-04-22 07:41:29 +02:00
let deletions = match get_deleted_messages ( get_deletions_query_params , & auth_token , & pool ) {
Ok ( deletions ) = > deletions ,
Err ( e ) = > {
let status_code = super ::errors ::status_code ( e ) ;
let response_body = models ::CompactPollResponseBody {
room_id ,
status_code : status_code . as_u16 ( ) ,
messages : vec ! [ ] ,
deletions : vec ! [ ] ,
moderators : vec ! [ ] ,
} ;
response_bodies . push ( response_body ) ;
continue ;
}
} ;
2021-04-16 07:02:43 +02:00
// Get the moderators
2021-04-22 07:41:29 +02:00
let moderators = match get_moderators ( & auth_token , & pool ) {
Ok ( moderators ) = > moderators ,
Err ( e ) = > {
let status_code = super ::errors ::status_code ( e ) ;
let response_body = models ::CompactPollResponseBody {
room_id ,
status_code : status_code . as_u16 ( ) ,
messages : vec ! [ ] ,
deletions : vec ! [ ] ,
moderators : vec ! [ ] ,
} ;
response_bodies . push ( response_body ) ;
continue ;
}
} ;
2021-04-16 07:02:43 +02:00
// Add to the response
2021-04-22 07:41:29 +02:00
let response_body = models ::CompactPollResponseBody {
room_id ,
status_code : StatusCode ::OK . as_u16 ( ) ,
deletions ,
messages ,
moderators ,
} ;
2021-04-16 07:02:43 +02:00
response_bodies . push ( response_body ) ;
}
// Return
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
results : Vec < models ::CompactPollResponseBody > ,
}
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , results : response_bodies } ;
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
2021-04-16 06:34:32 +02:00
}
2021-03-11 00:38:02 +01:00
// Utilities
2021-03-31 02:23:45 +02:00
fn get_pending_tokens (
2021-03-29 06:53:57 +02:00
public_key : & str , pool : & storage ::DatabaseConnectionPool ,
) -> Result < Vec < ( i64 , Vec < u8 > ) > , Rejection > {
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
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 ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't get pending tokens due to error: {}. " , e ) ;
2021-03-29 06:53:57 +02:00
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
let pending_tokens : Vec < ( i64 , Vec < u8 > ) > = rows . filter_map ( | result | result . ok ( ) ) . collect ( ) ;
return Ok ( pending_tokens ) ;
}
2021-03-31 02:23:45 +02:00
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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-11 00:06:09 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " 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-31 02:23:45 +02:00
fn is_moderator (
2021-03-25 01:38:06 +01:00
public_key : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < bool , Rejection > {
2021-03-31 02:23:45 +02:00
let public_keys = get_moderators_vector ( & pool ) ? ;
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-31 02:23:45 +02:00
fn get_banned_public_keys_vector (
2021-03-25 01:38:06 +01:00
pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> 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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-11 01:02:28 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " 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-31 02:23:45 +02:00
fn is_banned ( public_key : & str , pool : & storage ::DatabaseConnectionPool ) -> Result < bool , Rejection > {
let public_keys = get_banned_public_keys_vector ( & pool ) ? ;
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-25 00:56:16 +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
2021-03-25 00:56:16 +01:00
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-31 02:23:45 +02:00
fn get_public_key_for_auth_token (
2021-03-25 01:38:06 +01:00
auth_token : & str , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> 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-25 00:56:16 +01:00
let rows = match query . query_map ( params! [ auth_token ] , | row | Ok ( row . get ( 0 ) ? ) ) {
2021-03-17 23:35:51 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
2021-04-01 00:55:47 +02:00
error! ( " Couldn't query database due to error: {}. " , e ) ;
2021-03-17 23:35:51 +01:00
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
}
2021-03-31 02:23:45 +02:00
fn has_authorization_level (
2021-03-25 01:38:06 +01:00
auth_token : & str , level : AuthorizationLevel , pool : & storage ::DatabaseConnectionPool ,
2021-03-25 00:56:16 +01:00
) -> Result < ( bool , String ) , Rejection > {
2021-03-17 23:58:45 +01:00
// Check that we have a public key associated with the given auth token
2021-03-31 02:23:45 +02:00
let public_key_option = get_public_key_for_auth_token ( auth_token , pool ) ? ;
2021-03-26 03:49:38 +01:00
let public_key = public_key_option . ok_or ( warp ::reject ::custom ( Error ::NoAuthToken ) ) ? ;
2021-03-17 23:58:45 +01:00
// Check that the given public key isn't banned
2021-03-31 02:23:45 +02:00
if is_banned ( & public_key , pool ) ? {
2021-03-25 00:56:16 +01:00
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-17 23:58:45 +01:00
// 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-31 02:23:45 +02:00
if ! is_moderator ( & public_key , pool ) ? {
2021-03-25 00:56:16 +01:00
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-17 23:58:45 +01:00
return Ok ( ( true , public_key ) ) ;
}
} ;
}