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-19 00:09:13 +01:00
use std ::fs ;
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-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 } ;
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-25 04:05:46 +01:00
#[ derive(Debug, Deserialize, Serialize) ]
struct RoomInfo {
2021-03-26 00:02:48 +01:00
id : String ,
2021-03-25 04:05:46 +01:00
name : String ,
image_id : Option < String > ,
}
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
// 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-25 00:56:16 +01:00
match conn . execute ( & stmt , params! [ id , name ] ) {
2021-03-23 05:22:54 +01:00
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
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-25 04:05:46 +01:00
pub async fn get_room ( room_id : & str ) -> Result < Response , Rejection > {
// Get a connection
let pool = & storage ::MAIN_POOL ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Get the room info if possible
2021-03-26 00:02:48 +01:00
let raw_query =
format! ( " SELECT id, name, image_id 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-03-26 00:02:48 +01:00
Ok ( RoomInfo { id : row . get ( 0 ) ? , name : row . get ( 1 ) ? , image_id : row . get ( 2 ) . ok ( ) } )
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 ,
room : RoomInfo ,
}
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , room } ;
return Ok ( warp ::reply ::json ( & response ) . into_response ( ) ) ;
}
pub async fn get_all_rooms ( ) -> Result < Response , Rejection > {
// Get a connection
let pool = & storage ::MAIN_POOL ;
let conn = pool . get ( ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
// Get the room info if possible
2021-03-26 00:02:48 +01:00
let raw_query = format! ( " SELECT id, name, image_id FROM {} " , storage ::MAIN_TABLE ) ;
2021-03-25 04:05:46 +01:00
let mut query = conn . prepare ( & raw_query ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
2021-03-26 00:02:48 +01:00
let rows = match query . query_map ( params! [ ] , | row | {
Ok ( RoomInfo { id : row . get ( 0 ) ? , name : row . get ( 1 ) ? , image_id : row . get ( 2 ) . ok ( ) } )
} ) {
2021-03-25 04:05:46 +01:00
Ok ( rows ) = > rows ,
Err ( e ) = > {
println! ( " Couldn't get rooms due to error: {} . " , e ) ;
return Err ( warp ::reject ::custom ( Error ::DatabaseFailedInternally ) ) ;
}
} ;
let rooms : Vec < RoomInfo > = rows . filter_map ( | result | result . ok ( ) ) . collect ( ) ;
// Return
#[ derive(Debug, Deserialize, Serialize) ]
struct Response {
status_code : u16 ,
rooms : Vec < RoomInfo > ,
}
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-03-25 00:56:16 +01: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
let now = chrono ::Utc ::now ( ) . timestamp ( ) ;
2021-03-25 00:17:47 +01:00
// Check authorization level
2021-03-25 00:56:16 +01:00
let ( has_authorization_level , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
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 ) = > {
println! ( " Couldn't parse bytes from invalid base64 encoding due to error: {} . " , e ) ;
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-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 ) = > {
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-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 ) ;
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-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-03-25 00:56:16 +01: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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
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-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-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
}
// Authentication
2021-03-25 00:56:16 +01:00
pub async 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-03-23 23:12:54 +01:00
println! ( " 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-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-25 00:56:16 +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-25 00:56:16 +01:00
let symmetric_key =
crypto ::get_x25519_symmetric_key ( & public_key , & ephemeral_private_key ) . await ? ;
2021-03-16 06:25:59 +01:00
// Generate a random token
let mut token = [ 0 u8 ; 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 ) ? ;
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
) ;
let _ = match conn . execute ( & stmt , params! [ hex_public_key , now , token . to_vec ( ) ] ) {
2021-03-19 03:26:53 +01:00
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-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-25 00:56:16 +01:00
pub async 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-03-17 00:10:26 +01:00
println! ( " 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-03-17 00:10:26 +01:00
println! ( " 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-25 00:56:16 +01:00
let raw_query = format! (
" SELECT timestamp, token FROM {} WHERE public_key = (?1) AND timestamp > (?2) " ,
storage ::PENDING_TOKENS_TABLE
) ;
2021-03-19 03:26:53 +01:00
let mut query = conn . prepare ( & raw_query ) . map_err ( | _ | Error ::DatabaseFailedInternally ) ? ;
let now = chrono ::Utc ::now ( ) . timestamp ( ) ;
let expiration = now - storage ::PENDING_TOKEN_EXPIRATION ;
2021-03-25 00:56:16 +01:00
let rows = match query
. query_map ( params! [ public_key , expiration ] , | row | Ok ( ( row . get ( 0 ) ? , row . get ( 1 ) ? ) ) )
{
2021-03-19 03:26:53 +01:00
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
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 ) = > {
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 ) ;
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-03-25 01:38:06 +01:00
Err ( e ) = > println! ( " 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-25 00:56:16 +01:00
pub async 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 ) =
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-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 ) = > {
println! ( " Couldn't delete token due to error: {} . " , e ) ;
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-25 00:56:16 +01:00
pub async 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-03-11 04:20:36 +01:00
println! ( " 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 ) =
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-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 ) = > {
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 ) ;
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-25 00:56:16 +01:00
pub async fn get_messages (
2021-03-25 01:38:06 +01:00
query_params : HashMap < String , String > , 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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
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-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) ]
2021-03-24 00:02:53 +01:00
struct Response {
status_code : u16 ,
2021-03-25 01:38:06 +01:00
messages : Vec < models ::Message > ,
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 ( ) , messages } ;
2021-03-19 06:44:07 +01:00
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-25 00:56:16 +01:00
pub async 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 ) =
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-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 ) = > {
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 ) ) ? ;
2021-03-25 00:56:16 +01:00
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-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 ) = > {
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-25 00:56:16 +01:00
match tx . execute ( & stmt , params! [ row_id ] ) {
2021-03-15 00:16:06 +01:00
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-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-25 00:56:16 +01:00
pub async fn get_deleted_messages (
2021-03-25 01:38:06 +01:00
query_params : HashMap < String , String > , 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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
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-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) ]
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
ids : Vec < i64 > ,
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 ( ) , ids } ;
2021-03-19 06:44:07 +01:00
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-25 00:56:16 +01:00
pub async fn get_moderators (
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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-25 00:17:47 +01:00
// Return
2021-03-12 05:46:06 +01:00
let public_keys = get_moderators_vector ( pool ) . await ? ;
2021-03-21 23:49:54 +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
moderators : Vec < String > ,
2021-03-21 23:49:54 +01:00
}
2021-03-25 00:56:16 +01:00
let response = Response { status_code : StatusCode ::OK . as_u16 ( ) , moderators : public_keys } ;
2021-03-21 23:49:54 +01:00
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-25 00:56:16 +01:00
pub async 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-03-11 04:20:36 +01:00
println! ( " 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 , _ ) =
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-25 00:56:16 +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-25 00:56:16 +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-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-25 00:56:16 +01:00
pub async 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-03-11 04:20:36 +01:00
println! ( " 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 , _ ) =
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-25 00:56:16 +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-25 00:56:16 +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-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-25 00:56:16 +01:00
pub async 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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
if ! has_authorization_level {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-25 00:17:47 +01:00
// Return
2021-03-12 05:46:06 +01:00
let public_keys = get_banned_public_keys_vector ( pool ) . await ? ;
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-25 00:56:16 +01:00
pub async 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 , _ ) =
has_authorization_level ( auth_token , AuthorizationLevel ::Basic , pool ) . await ? ;
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 ) = > {
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) ]
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-03-11 00:38:02 +01:00
// Utilities
2021-03-25 00:56:16 +01:00
async fn get_moderators_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 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-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-25 00:56:16 +01:00
async 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-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-25 00:56:16 +01:00
async 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-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-25 00:56:16 +01:00
async fn is_banned (
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-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-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-25 00:56:16 +01:00
async 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 ) = > {
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
}
2021-03-25 00:56:16 +01:00
async 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-25 00:17:47 +01:00
let public_key_option = get_public_key_for_auth_token ( auth_token , pool ) . await ? ;
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-25 00:56:16 +01:00
if is_banned ( & public_key , pool ) . await ? {
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-25 00:56:16 +01:00
if ! is_moderator ( & public_key , pool ) . await ? {
return Err ( warp ::reject ::custom ( Error ::Unauthorized ) ) ;
}
2021-03-17 23:58:45 +01:00
return Ok ( ( true , public_key ) ) ;
}
} ;
}