store joined servers on auth server

This commit is contained in:
2024-09-21 10:55:01 -04:00
Unverified
parent aeb24f1cb4
commit 211050f57a
4 changed files with 516 additions and 431 deletions

View File

@@ -5,6 +5,7 @@ CREATE TABLE IF NOT EXISTS user (
email VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL,
new_email VARCHAR(255), new_email VARCHAR(255),
avatar TEXT NOT NULL, avatar TEXT NOT NULL,
servers TEXT NOT NULL,
login_code INT(6), login_code INT(6),
tokens TEXT, tokens TEXT,
google_oauth VARCHAR(255), google_oauth VARCHAR(255),

View File

@@ -9,7 +9,7 @@ use regex::Regex;
use sha3::{Digest, Sha3_256}; use sha3::{Digest, Sha3_256};
use sha3::digest::Update; use sha3::digest::Update;
use sqlx::{Pool, query, Sqlite}; use sqlx::{Pool, query, Sqlite};
use sqlx::sqlite::SqliteQueryResult; use sqlx::sqlite::{SqliteQueryResult, SqliteRow};
use tarpc::context::Context; use tarpc::context::Context;
use tracing::*; use tracing::*;
use tracing::log::__private_api::log; use tracing::log::__private_api::log;
@@ -19,506 +19,584 @@ use realm_shared::types::ErrorCode::*;
#[derive(Clone)] #[derive(Clone)]
pub struct RealmAuthServer { pub struct RealmAuthServer {
pub socket: SocketAddr, pub socket: SocketAddr,
pub db_pool: Pool<Sqlite>, pub db_pool: Pool<Sqlite>,
pub auth_email: AuthEmail, pub auth_email: AuthEmail,
pub template_html: String, pub template_html: String,
pub template_txt: String, pub template_txt: String,
pub domain: String, pub domain: String,
} }
impl RealmAuthServer { impl RealmAuthServer {
pub fn new(socket: SocketAddr, db_pool: Pool<Sqlite>, auth_email: AuthEmail) -> RealmAuthServer { pub fn new(socket: SocketAddr, db_pool: Pool<Sqlite>, auth_email: AuthEmail) -> RealmAuthServer {
RealmAuthServer { RealmAuthServer {
socket, socket,
db_pool, db_pool,
auth_email, auth_email,
template_html: std::fs::read_to_string("./login_email.html").expect("A login_email.html file is needed"), template_html: std::fs::read_to_string("./login_email.html").expect("A login_email.html file is needed"),
template_txt: std::fs::read_to_string("./login_email.txt").expect("A login_email.txt file is needed"), template_txt: std::fs::read_to_string("./login_email.txt").expect("A login_email.txt file is needed"),
domain: env::var("DOMAIN").expect("DOMAIN must be set"), domain: env::var("DOMAIN").expect("DOMAIN must be set"),
} }
} }
fn gen_login_code(&self) -> u16 { fn gen_login_code(&self) -> u16 {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut login_code: u16 = 0; let mut login_code: u16 = 0;
for n in 1..=6 { for n in 1..=6 {
if n == 1 { if n == 1 {
login_code += rng.gen_range(1..=9); login_code += rng.gen_range(1..=9);
} }
login_code += rng.gen_range(0..=9) * (10^n); login_code += rng.gen_range(0..=9) * (10^n);
} }
login_code login_code
} }
async fn is_username_taken(&self, username: &str) -> Result<bool, ErrorCode> { async fn is_username_taken(&self, username: &str) -> Result<bool, ErrorCode> {
let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE username = ?) AS does_exist", username).fetch_one(&self.db_pool).await; let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE username = ?) AS does_exist", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => Ok(row.does_exist != 0), Ok(row) => Ok(row.does_exist != 0),
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn is_email_taken(&self, email: &str) -> Result<bool, ErrorCode> { async fn is_email_taken(&self, email: &str) -> Result<bool, ErrorCode> {
let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE email = ?) AS does_exist", email).fetch_one(&self.db_pool).await; let result = query!("SELECT NOT EXISTS (SELECT 1 FROM user WHERE email = ?) AS does_exist", email).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => Ok(row.does_exist != 0), Ok(row) => Ok(row.does_exist != 0),
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn is_authorized(&self, username: &str, token: &str) -> Result<bool, ErrorCode> { async fn is_authorized(&self, username: &str, token: &str) -> Result<bool, ErrorCode> {
let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
let token_long: &str = &row.tokens.unwrap(); let token_long: &str = &row.tokens.unwrap();
let tokens = token_long.split(',').collect::<Vec<&str>>(); let tokens = token_long.split(',').collect::<Vec<&str>>();
for i in 0..tokens.len() { for i in 0..tokens.len() {
if tokens.get(i).unwrap() == &token { if tokens.get(i).unwrap() == &token {
return Ok(true) return Ok(true)
} }
} }
Ok(false) Ok(false)
}, },
Err(_) => Err(InvalidUsername), Err(_) => Err(InvalidUsername),
} }
} }
async fn send_login_message(&self, username: &str, email: &str, login_code: u16) -> Result<(), ErrorCode> { async fn send_login_message(&self, username: &str, email: &str, login_code: u16) -> Result<(), ErrorCode> {
let message = MessageBuilder::new() let message = MessageBuilder::new()
.from((self.auth_email.auth_name.clone(), self.auth_email.auth_username.clone())) .from((self.auth_email.auth_name.clone(), self.auth_email.auth_username.clone()))
.to(vec![ .to(vec![
(username, email), (username, email),
]) ])
.subject(format!("Realm confirmation code: {}", &login_code)) .subject(format!("Realm confirmation code: {}", &login_code))
.html_body(self.template_html.replace("{}", &login_code.to_string())) .html_body(self.template_html.replace("{}", &login_code.to_string()))
.text_body(self.template_txt.replace("{}", &login_code.to_string())); .text_body(self.template_txt.replace("{}", &login_code.to_string()));
let result = SmtpClientBuilder::new(&self.auth_email.server_address, self.auth_email.server_port) let result = SmtpClientBuilder::new(&self.auth_email.server_address, self.auth_email.server_port)
.implicit_tls(false) .implicit_tls(false)
.credentials(Credentials::new(&self.auth_email.auth_username, &self.auth_email.auth_password)) .credentials(Credentials::new(&self.auth_email.auth_username, &self.auth_email.auth_password))
.connect() .connect()
.await; .await;
match result { match result {
Ok(mut client) => { Ok(mut client) => {
let result = client.send(message).await; let result = client.send(message).await;
match result { match result {
Ok(_) => { Ok(_) => {
Ok(()) Ok(())
} }
Err(_) => { Err(_) => {
Err(UnableToSendMail) Err(UnableToSendMail)
} }
} }
} }
Err(_) => { Err(_) => {
Err(UnableToConnectToMail) Err(UnableToConnectToMail)
} }
} }
} }
async fn is_login_code_valid(&self, username: &str, login_code: u16) -> Result<bool, ErrorCode> { async fn is_login_code_valid(&self, username: &str, login_code: u16) -> Result<bool, ErrorCode> {
let result = query!("SELECT login_code FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await; let result = query!("SELECT login_code FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
if row.login_code.unwrap() as u16 != login_code { if row.login_code.unwrap() as u16 != login_code {
return Ok(false) return Ok(false)
} }
Ok(true) Ok(true)
} }
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
fn is_username_valid(&self, username: &str) -> bool { fn is_username_valid(&self, username: &str) -> bool {
if !username.starts_with('@') || !username.contains(':') { if !username.starts_with('@') || !username.contains(':') {
return false return false
} }
let name = &username[1..username.find(':').unwrap()]; let name = &username[1..username.find(':').unwrap()];
let domain = &username[username.find(':').unwrap()+1..]; let domain = &username[username.find(':').unwrap()+1..];
let re = Regex::new(r"^[a-zA-Z0-9]+$").unwrap(); let re = Regex::new(r"^[a-zA-Z0-9]+$").unwrap();
if !re.is_match(name) { if !re.is_match(name) {
return false return false
} }
if !domain.eq(&self.domain) { if !domain.eq(&self.domain) {
return false return false
} }
true true
} }
async fn reset_login_code(&self, username: &str) -> Result<(), ErrorCode> { async fn reset_login_code(&self, username: &str) -> Result<(), ErrorCode> {
let result = query!("UPDATE user SET login_code = NULL WHERE username = ?", username).execute(&self.db_pool).await; let result = query!("UPDATE user SET login_code = NULL WHERE username = ?", username).execute(&self.db_pool).await;
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
} }
impl RealmAuth for RealmAuthServer { impl RealmAuth for RealmAuthServer {
async fn test(self, _: Context, name: String) -> String { async fn test(self, _: Context, name: String) -> String {
format!("Hello {} auth!", name) format!("Hello {} auth!", name)
} }
async fn server_token_validation(self, _: Context, server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool { async fn server_token_validation(self, _: Context, server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool {
info!("API Request: server_token_validation( server_token -> {}, username -> {}, server_id -> {}, domain -> {}, tarpc_port -> {} )", info!("API Request: server_token_validation( server_token -> {}, username -> {}, server_id -> {}, domain -> {}, tarpc_port -> {} )",
server_token, username, server_id, domain, tarpc_port); server_token, username, server_id, domain, tarpc_port);
let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
let token_long: &str = &row.tokens.unwrap(); let token_long: &str = &row.tokens.unwrap();
let tokens = token_long.split(',').collect::<Vec<&str>>(); let tokens = token_long.split(',').collect::<Vec<&str>>();
for token in tokens { for token in tokens {
let hash = Sha3_256::new().chain(format!("{}{}{}{}", token, server_id, domain, tarpc_port)).finalize(); let hash = Sha3_256::new().chain(format!("{}{}{}{}", token, server_id, domain, tarpc_port)).finalize();
if hex::encode(hash) == server_token { if hex::encode(hash) == server_token {
return true return true
} }
} }
false false
}, },
Err(_) => false, Err(_) => false,
} }
} }
async fn create_account_flow(self, _: Context, username: String, email: String) -> Result<(), ErrorCode> { async fn create_account_flow(self, _: Context, username: String, email: String) -> Result<(), ErrorCode> {
info!("API Request: create_account_flow( username -> {}, email -> {} )", username, email); info!("API Request: create_account_flow( username -> {}, email -> {} )", username, email);
if !self.is_username_valid(&username) { if !self.is_username_valid(&username) {
return Err(InvalidUsername) return Err(InvalidUsername)
} }
if self.is_username_taken(&username).await? { if self.is_username_taken(&username).await? {
return Err(UsernameTaken) return Err(UsernameTaken)
} }
if self.is_email_taken(&email).await? { if self.is_email_taken(&email).await? {
return Err(EmailTaken) return Err(EmailTaken)
} }
let code = self.gen_login_code(); let code = self.gen_login_code();
self.send_login_message(&username, &email, code).await?; self.send_login_message(&username, &email, code).await?;
let result = query!("INSERT INTO user (username, email, avatar, login_code, tokens) VALUES (?, ?, '', ?, '')", username, email, code) let result = query!("INSERT INTO user (username, email, avatar, login_code, tokens) VALUES (?, ?, '', ?, '')", username, email, code)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => Err(Error) Err(_) => Err(Error)
} }
} }
async fn create_login_flow(self, _: Context, mut username: Option<String>, mut email: Option<String>) -> Result<(), ErrorCode> { async fn create_login_flow(self, _: Context, mut username: Option<String>, mut email: Option<String>) -> Result<(), ErrorCode> {
info!("API Request: create_login_flow( username -> {}, email -> {} )", username.clone().unwrap_or("None".to_string()), email.clone().unwrap_or("None".to_string())); info!("API Request: create_login_flow( username -> {}, email -> {} )", username.clone().unwrap_or("None".to_string()), email.clone().unwrap_or("None".to_string()));
if username.is_none() && email.is_none() { if username.is_none() && email.is_none() {
return Err(Error) return Err(Error)
} }
if username.is_none() { if username.is_none() {
let tmp = email.clone().unwrap(); let tmp = email.clone().unwrap();
let result = query!("SELECT username FROM user WHERE email = ?;", tmp) let result = query!("SELECT username FROM user WHERE email = ?;", tmp)
.fetch_one(&self.db_pool).await; .fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
username = Some(row.username); username = Some(row.username);
} }
Err(_) => return Err(InvalidEmail) Err(_) => return Err(InvalidEmail)
} }
} }
if email.clone().is_none() { if email.clone().is_none() {
let tmp = username.clone().unwrap(); let tmp = username.clone().unwrap();
let result = query!("SELECT email FROM user WHERE username = ?;", tmp) let result = query!("SELECT email FROM user WHERE username = ?;", tmp)
.fetch_one(&self.db_pool).await; .fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
email = Some(row.email); email = Some(row.email);
} }
Err(_) => return Err(InvalidUsername) Err(_) => return Err(InvalidUsername)
} }
} }
let code = self.gen_login_code(); let code = self.gen_login_code();
let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username) let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
match result { match result {
Ok(_) => self.send_login_message(&username.unwrap(), &email.unwrap(), code).await, Ok(_) => self.send_login_message(&username.unwrap(), &email.unwrap(), code).await,
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn finish_login_flow(self, _: Context, username: String, login_code: u16) -> Result<String, ErrorCode> { async fn finish_login_flow(self, _: Context, username: String, login_code: u16) -> Result<String, ErrorCode> {
info!("API Request: finish_login_flow( username -> {}, login_code -> {} )", username, login_code); info!("API Request: finish_login_flow( username -> {}, login_code -> {} )", username, login_code);
if !self.is_login_code_valid(&username, login_code).await? { if !self.is_login_code_valid(&username, login_code).await? {
error!("Unauthorized request made for finish_login_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code); error!("Unauthorized request made for finish_login_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
return Err(InvalidLoginCode) return Err(InvalidLoginCode)
} }
self.reset_login_code(&username).await?; self.reset_login_code(&username).await?;
let _ = query!("UPDATE user SET login_code = NULL WHERE username = ?", username).execute(&self.db_pool).await; let _ = query!("UPDATE user SET login_code = NULL WHERE username = ?", username).execute(&self.db_pool).await;
let hash = Sha3_256::new().chain(format!("{}{}{}", username, login_code, Utc::now().to_utc())).finalize(); let hash = Sha3_256::new().chain(format!("{}{}{}", username, login_code, Utc::now().to_utc())).finalize();
let token = hex::encode(hash); let token = hex::encode(hash);
let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
let token_long: &str = &row.tokens.unwrap(); let token_long: &str = &row.tokens.unwrap();
let mut tokens = token_long.split(',').collect::<Vec<&str>>(); let mut tokens = token_long.split(',').collect::<Vec<&str>>();
tokens.push(&token); tokens.push(&token);
let mega_token = tokens.join(","); let mega_token = tokens.join(",");
let result = query!("UPDATE user SET tokens = ? WHERE username = ?", mega_token, username) let result = query!("UPDATE user SET tokens = ? WHERE username = ?", mega_token, username)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
match result { match result {
Ok(_) => Ok(token), Ok(_) => Ok(token),
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn change_email_flow(self, _: Context, username: String, new_email: String, token: String) -> Result<(), ErrorCode> { async fn change_email_flow(self, _: Context, username: String, new_email: String, token: String) -> Result<(), ErrorCode> {
info!("API Request: change_email_flow( username -> {}, new_email -> {}, token -> {} )", username, new_email, token); info!("API Request: change_email_flow( username -> {}, new_email -> {}, token -> {} )", username, new_email, token);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized) return Err(Unauthorized)
} }
if self.is_email_taken(&new_email).await? { if self.is_email_taken(&new_email).await? {
return Err(EmailTaken) return Err(EmailTaken)
} }
let _ = query!("UPDATE user SET new_email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await.unwrap(); let _ = query!("UPDATE user SET new_email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await.unwrap();
let code = self.gen_login_code(); let code = self.gen_login_code();
let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username) let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
match result { match result {
Ok(_) => self.send_login_message(&username, &new_email, code).await, Ok(_) => self.send_login_message(&username, &new_email, code).await,
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn finish_change_email_flow(self, _: Context, username: String, new_email: String, token: String, login_code: u16) -> Result<(), ErrorCode> { async fn finish_change_email_flow(self, _: Context, username: String, new_email: String, token: String, login_code: u16) -> Result<(), ErrorCode> {
info!("API Request: finish_change_email_flow( username -> {}, new_email -> {}, token -> {}, login_code -> {} )", username, new_email, token, login_code); info!("API Request: finish_change_email_flow( username -> {}, new_email -> {}, token -> {}, login_code -> {} )", username, new_email, token, login_code);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
error!("Unauthorized request made for finish_change_email_flow() (bad token)! username -> {}, token -> {}", username, token); error!("Unauthorized request made for finish_change_email_flow() (bad token)! username -> {}, token -> {}", username, token);
return Err(Unauthorized) return Err(Unauthorized)
} }
if self.is_email_taken(&new_email).await? { if self.is_email_taken(&new_email).await? {
error!("Email already taken for email change (but its the end of the flow?!) username -> {}, new_email -> {}", username, new_email); error!("Email already taken for email change (but its the end of the flow?!) username -> {}, new_email -> {}", username, new_email);
return Err(EmailTaken) return Err(EmailTaken)
} }
if !self.is_login_code_valid(&username, login_code).await? { if !self.is_login_code_valid(&username, login_code).await? {
error!("Unauthorized request made for finish_change_email_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code); error!("Unauthorized request made for finish_change_email_flow() (bad login code)! username -> {}, login_code -> {}", username, login_code);
return Err(InvalidLoginCode) return Err(InvalidLoginCode)
} }
let _ = query!("UPDATE user SET new_email = NULL WHERE username = ?", username).execute(&self.db_pool).await; let _ = query!("UPDATE user SET new_email = NULL WHERE username = ?", username).execute(&self.db_pool).await;
let _ = query!("UPDATE user SET email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await; let _ = query!("UPDATE user SET email = ? WHERE username = ?", new_email, username).execute(&self.db_pool).await;
self.reset_login_code(&username).await?; self.reset_login_code(&username).await?;
Ok(()) Ok(())
} }
// async fn change_username(self, _: Context, username: String, token: String, new_username: String) -> Result<(), ErrorCode> { // TODO: find a way of supporting, post-capstone work
// info!("API Request: change_username( username -> {}, token -> {}, new_username -> {} )", username, token, new_username); // async fn change_username(self, _: Context, username: String, token: String, new_username: String) -> Result<(), ErrorCode> {
// // info!("API Request: change_username( username -> {}, token -> {}, new_username -> {} )", username, token, new_username);
// if !self.is_authorized(&username, &token).await? { //
// error!("Unauthorized request made for change_username()! username -> {}, token -> {}", username, token); // if !self.is_authorized(&username, &token).await? {
// return Err(Unauthorized) // error!("Unauthorized request made for change_username()! username -> {}, token -> {}", username, token);
// } // return Err(Unauthorized)
// // }
// if !self.is_username_valid(&new_username) { //
// error!("Malformed username in request for change_username()! new_username -> {}", new_username); // if !self.is_username_valid(&new_username) {
// return Err(InvalidUsername) // error!("Malformed username in request for change_username()! new_username -> {}", new_username);
// } // return Err(InvalidUsername)
// // }
// if self.is_username_taken(&new_username).await? { //
// error!("Username is taken for change_username()! new_username -> {}", new_username); // if self.is_username_taken(&new_username).await? {
// return Err(UsernameTaken) // error!("Username is taken for change_username()! new_username -> {}", new_username);
// } // return Err(UsernameTaken)
// // }
// let result = query!("UPDATE user SET username = ? WHERE username = ?", new_username, username).execute(&self.db_pool).await; //
// match result { // let result = query!("UPDATE user SET username = ? WHERE username = ?", new_username, username).execute(&self.db_pool).await;
// Ok(_) => Ok(()), // match result {
// Err(_) => Err(Error) // Ok(_) => Ok(()),
// } // Err(_) => Err(Error)
// } // }
// }
async fn change_avatar(self, _: Context, username: String, token: String, new_avatar: String) -> Result<(), ErrorCode> { async fn change_avatar(self, _: Context, username: String, token: String, new_avatar: String) -> Result<(), ErrorCode> {
info!("API Request: change_avatar( username -> {}, token -> {}, new_avatar -> {} )", username, token, new_avatar); info!("API Request: change_avatar( username -> {}, token -> {}, new_avatar -> {} )", username, token, new_avatar);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
error!("Unauthorized request made for change_avatar()! username -> {}, token -> {}", username, token); error!("Unauthorized request made for change_avatar()! username -> {}, token -> {}", username, token);
return Err(Unauthorized) return Err(Unauthorized)
} }
let result = query!("UPDATE user SET avatar = ? WHERE username = ?", new_avatar, username).execute(&self.db_pool).await; let result = query!("UPDATE user SET avatar = ? WHERE username = ?", new_avatar, username).execute(&self.db_pool).await;
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => Err(Error) Err(_) => Err(Error)
} }
} }
async fn get_all_data(self, _: Context, username: String, token: String) -> Result<AuthUser, ErrorCode> { async fn get_all_data(self, _: Context, username: String, token: String) -> Result<AuthUser, ErrorCode> {
info!("API Request: get_all_data( username -> {}, token -> {} )", username, token); info!("API Request: get_all_data( username -> {}, token -> {} )", username, token);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
error!("Unauthorized request made for get_all_data()! username -> {}, token -> {}", username, token); error!("Unauthorized request made for get_all_data()! username -> {}, token -> {}", username, token);
return Err(Unauthorized) return Err(Unauthorized)
} }
let result = query!(r"SELECT * FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!(r"SELECT * FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
Ok(AuthUser { Ok(AuthUser {
id: row.id, id: row.id,
username: row.username, username: row.username,
email: row.email, email: row.email,
avatar: row.avatar, avatar: row.avatar,
login_code: None, servers: row.servers,
bigtoken: row.tokens, login_code: None,
google_oauth: row.google_oauth, bigtoken: row.tokens,
apple_oauth: row.apple_oauth, google_oauth: row.google_oauth,
github_oauth: row.github_oauth, apple_oauth: row.apple_oauth,
discord_oauth: row.discord_oauth, github_oauth: row.github_oauth,
}) discord_oauth: row.discord_oauth,
} })
Err(_) => { }
error!("Invalid username in request for get_all_data()! username -> {}", username); Err(_) => {
Err(InvalidUsername) error!("Invalid username in request for get_all_data()! username -> {}", username);
} Err(InvalidUsername)
} }
} }
}
async fn sign_out(self, _: Context, username: String, token: String) -> Result<(), ErrorCode> { async fn sign_out(self, _: Context, username: String, token: String) -> Result<(), ErrorCode> {
info!("API Request: sign_out( username -> {}, token -> {} )", username, token); info!("API Request: sign_out( username -> {}, token -> {} )", username, token);
let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT tokens FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result { match result {
Ok(row) => { Ok(row) => {
let token_long: &str = &row.tokens.unwrap(); let token_long: &str = &row.tokens.unwrap();
let mut tokens = token_long.split(',').collect::<Vec<&str>>(); let mut tokens = token_long.split(',').collect::<Vec<&str>>();
for i in 0..tokens.len() { for i in 0..tokens.len() {
if tokens.get(i).unwrap().eq(&token.as_str()) { if tokens.get(i).unwrap().eq(&token.as_str()) {
tokens.remove(i); tokens.remove(i);
let mega_token = tokens.join(",").to_string(); let mega_token = tokens.join(",").to_string();
let result = query!("UPDATE user SET tokens = ? WHERE username = ?", mega_token, username) let result = query!("UPDATE user SET tokens = ? WHERE username = ?", mega_token, username)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
return match result { return match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => { Err(_) => {
error!("Unable to update tokens on sign_out()! \ error!("Unable to update tokens on sign_out()! \
username -> {}, (previous) token_long -> {}, mega_token -> {}, token -> {}", username -> {}, (previous) token_long -> {}, mega_token -> {}, token -> {}",
username, token_long, mega_token, token); username, token_long, mega_token, token);
Err(Error) Err(Error)
} }
}; };
} }
} }
error!("Unauthorized request made for sign_out()! username -> {}, token -> {}", username, token); error!("Unauthorized request made for sign_out()! username -> {}, token -> {}", username, token);
Err(Unauthorized) Err(Unauthorized)
}, },
Err(_) => { Err(_) => {
error!("Invalid username in request for get_avatar_for_user()! username -> {}", username); error!("Invalid username in request for get_avatar_for_user()! username -> {}", username);
Err(InvalidUsername) Err(InvalidUsername)
}, },
} }
} }
async fn delete_account_flow(self, _: Context, username: String, token: String) -> Result<(), ErrorCode> { async fn delete_account_flow(self, _: Context, username: String, token: String) -> Result<(), ErrorCode> {
info!("API Request: delete_account_flow( username -> {}, token -> {} )", username, token); info!("API Request: delete_account_flow( username -> {}, token -> {} )", username, token);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized) return Err(Unauthorized)
} }
let email = match query!("SELECT email FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await { let email = match query!("SELECT email FROM user WHERE username = ?;", username).fetch_one(&self.db_pool).await {
Ok(row) => Ok(row.email), Ok(row) => Ok(row.email),
Err(_) => Err(InvalidUsername), Err(_) => Err(InvalidUsername),
}?; }?;
let code = self.gen_login_code(); let code = self.gen_login_code();
let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username) let result = query!("UPDATE user SET login_code = ? WHERE username = ?;", code, username)
.execute(&self.db_pool).await; .execute(&self.db_pool).await;
match result { match result {
Ok(_) => self.send_login_message(&username, &email, code).await, Ok(_) => self.send_login_message(&username, &email, code).await,
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn finish_delete_account_flow(self, _: Context, username: String, token: String, login_code: u16) -> Result<(), ErrorCode> { async fn finish_delete_account_flow(self, _: Context, username: String, token: String, login_code: u16) -> Result<(), ErrorCode> {
info!("API Request: finish_delete_account_flow( username -> {}, token -> {}, login_code -> {} )", username, token, login_code); info!("API Request: finish_delete_account_flow( username -> {}, token -> {}, login_code -> {} )", username, token, login_code);
if !self.is_authorized(&username, &token).await? { if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized) return Err(Unauthorized)
} }
if !self.is_login_code_valid(&username, login_code).await? { if !self.is_login_code_valid(&username, login_code).await? {
return Err(InvalidLoginCode) return Err(InvalidLoginCode)
} }
self.reset_login_code(&username).await?; self.reset_login_code(&username).await?;
let result = query!("DELETE FROM user WHERE username = ?", username).execute(&self.db_pool).await; let result = query!("DELETE FROM user WHERE username = ?", username).execute(&self.db_pool).await;
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(_) => Err(InvalidUsername) Err(_) => Err(InvalidUsername)
} }
} }
async fn get_avatar_for_user(self, _: Context, username: String) -> Result<String, ErrorCode> { async fn add_server(self, _: Context, username: String, token: String, domain: String) -> Result<(), ErrorCode> {
info!("API Request: get_avatar_for_user( username -> {} )", username); if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized)
}
let result = query!("SELECT avatar FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await; let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => {
let mut vec_servers = row.servers.split('|').collect::<Vec<&str>>();
for server in &vec_servers {
if server.eq(&domain) {
return Err(AlreadyJoinedServer)
}
}
match result { vec_servers.push(&domain);
Ok(row) => Ok(row.avatar), let new_servers = vec_servers.join("|");
Err(_) => {
error!("Invalid username in request for get_avatar_for_user()! username -> {}", username); let result = query!("UPDATE user SET servers = ? WHERE username = ?", new_servers, username).fetch_one(&self.db_pool).await;
Err(InvalidUsername) match result {
} Ok(_) => Ok(()),
} Err(_) => Err(Error)
} }
}
Err(_) => Err(Error)
}
}
async fn remove_server(self, _: Context, username: String, token: String, domain: String) -> Result<(), ErrorCode> {
if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized)
}
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => {
let mut vec_servers = row.servers.split('|').collect::<Vec<&str>>();
for i in 0..vec_servers.len() {
if vec_servers.get(i).unwrap().eq(&domain) {
vec_servers.remove(i);
let new_servers = vec_servers.join("|");
let result = query!("UPDATE user SET servers = ? WHERE username = ?", new_servers, username).fetch_one(&self.db_pool).await;
return match result {
Ok(_) => Ok(()),
Err(_) => Err(Error)
}
}
}
Err(NotInServer)
}
Err(_) => Err(Error)
}
}
async fn get_joined_servers(self, _: Context, username: String, token: String) -> Result<Vec<String>, ErrorCode> {
if !self.is_authorized(&username, &token).await? {
return Err(Unauthorized)
}
let result = query!("SELECT servers FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => {
let vec_servers = row.servers.split('|').collect::<Vec<&str>>();
let mut servers = Vec::new();
for server in vec_servers {
servers.push(server.to_string())
}
Ok(servers)
}
Err(_) => Err(Error)
}
}
async fn get_avatar_for_user(self, _: Context, username: String) -> Result<String, ErrorCode> {
info!("API Request: get_avatar_for_user( username -> {} )", username);
let result = query!("SELECT avatar FROM user WHERE username = ?", username).fetch_one(&self.db_pool).await;
match result {
Ok(row) => Ok(row.avatar),
Err(_) => {
error!("Invalid username in request for get_avatar_for_user()! username -> {}", username);
Err(InvalidUsername)
}
}
}
} }

View File

@@ -18,6 +18,9 @@ pub trait RealmAuth {
async fn sign_out(username: String, token: String) -> Result<(), ErrorCode>; async fn sign_out(username: String, token: String) -> Result<(), ErrorCode>;
async fn delete_account_flow(username: String, token: String) -> Result<(), ErrorCode>; async fn delete_account_flow(username: String, token: String) -> Result<(), ErrorCode>;
async fn finish_delete_account_flow(username: String, token: String, login_code: u16) -> Result<(), ErrorCode>; async fn finish_delete_account_flow(username: String, token: String, login_code: u16) -> Result<(), ErrorCode>;
async fn add_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>;
async fn remove_server(username: String, token: String, domain: String) -> Result<(), ErrorCode>;
async fn get_joined_servers(username: String, token: String) -> Result<Vec<String>, ErrorCode>;
//NOTE: Anyone can call //NOTE: Anyone can call
async fn get_avatar_for_user(username: String) -> Result<String, ErrorCode>; async fn get_avatar_for_user(username: String) -> Result<String, ErrorCode>;
@@ -30,6 +33,7 @@ pub struct AuthUser {
pub username: String, pub username: String,
pub email: String, pub email: String,
pub avatar: String, pub avatar: String,
pub servers: String,
pub login_code: Option<u16>, pub login_code: Option<u16>,
pub bigtoken: Option<String>, pub bigtoken: Option<String>,
pub google_oauth: Option<String>, pub google_oauth: Option<String>,

View File

@@ -13,6 +13,8 @@ pub enum ErrorCode {
InvalidToken, InvalidToken,
UnableToConnectToMail, UnableToConnectToMail,
UnableToSendMail, UnableToSendMail,
AlreadyJoinedServer,
NotInServer,
MessageNotFound, MessageNotFound,
RoomNotFound, RoomNotFound,