Good progress
TOOD: Maybe shared library for ErrorCode? Emails, Account Creation, Username formatting, OAuth Support for all the ones we want, Testing
This commit is contained in:
@@ -15,3 +15,4 @@ dotenvy = "0.15"
|
|||||||
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls", "mysql", "chrono" ] }
|
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls", "mysql", "chrono" ] }
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
rand = "0.8.5"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
use sha3::{Digest, Sha3_256};
|
use sha3::{Digest, Sha3_256};
|
||||||
use sha3::digest::Update;
|
use sha3::digest::Update;
|
||||||
use sqlx::{MySql, Pool, Row};
|
use sqlx::{MySql, Pool, Row};
|
||||||
use tarpc::context::Context;
|
use tarpc::context::Context;
|
||||||
|
|
||||||
use crate::types::{AuthUser, ErrorCode, RealmAuth};
|
use crate::types::{AuthUser, ErrorCode, RealmAuth};
|
||||||
|
use crate::types::ErrorCode::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RealmAuthServer {
|
pub struct RealmAuthServer {
|
||||||
@@ -13,6 +15,8 @@ pub struct RealmAuthServer {
|
|||||||
pub db_pool: Pool<MySql>,
|
pub db_pool: Pool<MySql>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: USERNAME FORMATTING!
|
||||||
|
|
||||||
impl RealmAuthServer {
|
impl RealmAuthServer {
|
||||||
pub fn new(socket: SocketAddr, db_pool: Pool<MySql>) -> RealmAuthServer {
|
pub fn new(socket: SocketAddr, db_pool: Pool<MySql>) -> RealmAuthServer {
|
||||||
RealmAuthServer {
|
RealmAuthServer {
|
||||||
@@ -20,6 +24,63 @@ impl RealmAuthServer {
|
|||||||
db_pool,
|
db_pool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gen_login_code(&self) -> u16 {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut login_code: u16 = 0;
|
||||||
|
|
||||||
|
for n in 1..=6 {
|
||||||
|
if n == 1 {
|
||||||
|
login_code += rng.gen_range(1..=9);
|
||||||
|
}
|
||||||
|
login_code += rng.gen_range(0..=9) * (10^n);
|
||||||
|
}
|
||||||
|
|
||||||
|
login_code
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_username_taken(&self, username: &str) -> Result<bool, ErrorCode> {
|
||||||
|
let result = sqlx::query("SELECT NOT EXISTS (SELECT 1 FROM user WHERE username = ?) AS does_exist")
|
||||||
|
.bind(username)
|
||||||
|
.fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(row) => Ok(row.try_get("does_exist").unwrap()),
|
||||||
|
Err(_) => Err(InvalidUsername)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_email_taken(&self, email: &str) -> Result<bool, ErrorCode> {
|
||||||
|
let result = sqlx::query("SELECT NOT EXISTS (SELECT 1 FROM user WHERE email = ?) AS does_exist")
|
||||||
|
.bind(email)
|
||||||
|
.fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(row) => Ok(row.try_get("does_exist").unwrap()),
|
||||||
|
Err(_) => Err(InvalidUsername)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_authorized(&self, username: &str, token: &str) -> Result<bool, ErrorCode> {
|
||||||
|
let result = sqlx::query("SELECT tokens FROM user WHERE username = ?")
|
||||||
|
.bind(username).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(row) => {
|
||||||
|
let token_long: &str = row.try_get("tokens").unwrap();
|
||||||
|
let tokens = token_long.split(',').collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
for i in 0..tokens.len() {
|
||||||
|
if tokens.get(i).unwrap() == &token {
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
},
|
||||||
|
Err(_) => Err(InvalidUsername),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealmAuth for RealmAuthServer {
|
impl RealmAuth for RealmAuthServer {
|
||||||
@@ -48,39 +109,159 @@ impl RealmAuth for RealmAuthServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_account(self, _: Context, username: String, email: String, avatar: String) -> Result<String, ErrorCode> {
|
async fn create_account_flow(self, _: Context, username: String, email: String) -> ErrorCode {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn finish_account_flow(self, _: Context, username: String, login_code: u16, avatar: String) -> Result<String, ErrorCode> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_login_flow(self, _: Context, username: String) -> ErrorCode {
|
async fn create_login_flow(self, _: Context, username: String) -> ErrorCode {
|
||||||
|
let result = sqlx::query("UPDATE user SET login_code = ? WHERE username = ?;")
|
||||||
|
.bind(self.gen_login_code())
|
||||||
|
.bind(username)
|
||||||
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
todo!() //TODO: Emails!
|
||||||
|
},
|
||||||
|
Err(_) => InvalidUsername
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn finish_login_flow(self, _: Context, username: String, login_code: u16) -> Result<String, ErrorCode> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_token_from_login(self, _: Context, username: String, login_code: u16) -> Result<String, ErrorCode> {
|
async fn change_email_flow(self, _: Context, username: String, new_email: String, token: String) -> ErrorCode {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn change_email_flow(self, _: Context, username: String, token: String) -> ErrorCode {
|
async fn finish_change_email_flow(self, _: Context, username: String, token: String, login_code: u16) -> ErrorCode {
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resolve_email_flow(self, _: Context, username: String, token: String, login_code: u16, new_email: String) -> ErrorCode {
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn change_username(self, _: Context, username: String, token: String, new_username: String) -> ErrorCode {
|
async fn change_username(self, _: Context, username: String, token: String, new_username: String) -> ErrorCode {
|
||||||
todo!()
|
let result = self.is_authorized(&username, &token).await;
|
||||||
|
match result {
|
||||||
|
Ok(authorized) => {
|
||||||
|
if !authorized {
|
||||||
|
return Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => return error
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self.is_username_taken(&new_username).await;
|
||||||
|
match result {
|
||||||
|
Ok(is_taken) => {
|
||||||
|
if is_taken {
|
||||||
|
return UsernameTaken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => return error
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = sqlx::query("UPDATE user SET username = ? WHERE username = ?")
|
||||||
|
.bind(&new_username)
|
||||||
|
.bind(&username).execute(&self.db_pool).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => NoError,
|
||||||
|
Err(_) => Error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn change_avatar(self, _: Context, username: String, token: String, avatar: String) -> ErrorCode {
|
async fn change_avatar(self, _: Context, username: String, token: String, new_avatar: String) -> ErrorCode {
|
||||||
todo!()
|
let result = self.is_authorized(&username, &token).await;
|
||||||
|
match result {
|
||||||
|
Ok(authorized) => {
|
||||||
|
if !authorized {
|
||||||
|
return Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => return error
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = sqlx::query("UPDATE user SET avatar = ? WHERE username = ?")
|
||||||
|
.bind(&new_avatar)
|
||||||
|
.bind(&username).execute(&self.db_pool).await;
|
||||||
|
match result {
|
||||||
|
Ok(_) => NoError,
|
||||||
|
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> {
|
||||||
todo!()
|
let result = self.is_authorized(&username, &token).await;
|
||||||
|
match result {
|
||||||
|
Ok(authorized) => {
|
||||||
|
if !authorized {
|
||||||
|
return Err(Unauthorized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => return Err(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = sqlx::query("SELECT * FROM user WHERE username = ?")
|
||||||
|
.bind(&username).fetch_one(&self.db_pool).await;
|
||||||
|
match result {
|
||||||
|
Ok(row) => {
|
||||||
|
Ok(AuthUser {
|
||||||
|
id: row.try_get("id").unwrap(),
|
||||||
|
username: row.try_get("username").unwrap(),
|
||||||
|
email: row.try_get("email").unwrap(),
|
||||||
|
avatar: row.try_get("avatar").unwrap(),
|
||||||
|
login_code: None,
|
||||||
|
bigtoken: row.try_get("tokens").unwrap(),
|
||||||
|
google_oauth: row.try_get("google_oauth").unwrap(),
|
||||||
|
apple_oauth: row.try_get("apple_oauth").unwrap(),
|
||||||
|
github_oauth: row.try_get("github_oauth").unwrap(),
|
||||||
|
discord_oauth: row.try_get("discord_oauth").unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(_) => Err(InvalidUsername)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sign_out(self, _: Context, username: String, token: String) -> ErrorCode {
|
||||||
|
let result = sqlx::query("SELECT tokens FROM user WHERE username = ?")
|
||||||
|
.bind(&username).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(row) => {
|
||||||
|
let token_long: &str = row.try_get("tokens").unwrap();
|
||||||
|
let mut tokens = token_long.split(',').collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
for i in 0..tokens.len() {
|
||||||
|
if tokens.get(i).unwrap().eq(&token.as_str()) {
|
||||||
|
tokens.remove(i);
|
||||||
|
|
||||||
|
let result = sqlx::query("UPDATE user SET tokens = ? WHERE username = ?")
|
||||||
|
.bind(tokens.join(","))
|
||||||
|
.bind(&username)
|
||||||
|
.execute(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => NoError,
|
||||||
|
Err(_) => Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Unauthorized
|
||||||
|
},
|
||||||
|
Err(_) => InvalidUsername,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_avatar_for_user(self, _: Context, username: String) -> Result<String, ErrorCode> {
|
async fn get_avatar_for_user(self, _: Context, username: String) -> Result<String, ErrorCode> {
|
||||||
todo!()
|
let result = sqlx::query("SELECT tokens FROM user WHERE username = ?").bind(username).fetch_one(&self.db_pool).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(row) => Ok(row.try_get("avatar").unwrap_or("".to_string())),
|
||||||
|
Err(_) => Err(InvalidUsername)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,16 +4,18 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub trait RealmAuth {
|
pub trait RealmAuth {
|
||||||
async fn test(name: String) -> String;
|
async fn test(name: String) -> String;
|
||||||
async fn server_token_validation(server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool;
|
async fn server_token_validation(server_token: String, username: String, server_id: String, domain: String, tarpc_port: u16) -> bool;
|
||||||
async fn create_account(username: String, email: String, avatar: String) -> Result<String, ErrorCode>;
|
async fn create_account_flow(username: String, email: String) -> ErrorCode; //NOTE: Still require sign in flow
|
||||||
|
async fn finish_account_flow(username: String, login_code: u16, avatar: String) -> Result<String, ErrorCode>;
|
||||||
async fn create_login_flow(username: String) -> ErrorCode;
|
async fn create_login_flow(username: String) -> ErrorCode;
|
||||||
async fn create_token_from_login(username: String, login_code: u16) -> Result<String, ErrorCode>;
|
async fn finish_login_flow(username: String, login_code: u16) -> Result<String, ErrorCode>;
|
||||||
|
|
||||||
//NOTE: Need to be the user
|
//NOTE: Need to be the user
|
||||||
async fn change_email_flow(username: String, token: String) -> ErrorCode;
|
async fn change_email_flow(username: String, new_email: String, token: String) -> ErrorCode;
|
||||||
async fn resolve_email_flow(username: String, token: String, login_code: u16, new_email: String) -> ErrorCode;
|
async fn finish_change_email_flow(username: String, token: String, login_code: u16) -> ErrorCode;
|
||||||
async fn change_username(username: String, token: String, new_username: String) -> ErrorCode;
|
async fn change_username(username: String, token: String, new_username: String) -> ErrorCode;
|
||||||
async fn change_avatar(username: String, token: String, avatar: String) -> ErrorCode;
|
async fn change_avatar(username: String, token: String, new_avatar: String) -> ErrorCode;
|
||||||
async fn get_all_data(username: String, token: String) -> Result<AuthUser, ErrorCode>;
|
async fn get_all_data(username: String, token: String) -> Result<AuthUser, ErrorCode>;
|
||||||
|
async fn sign_out(username: String, token: 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>;
|
||||||
@@ -27,17 +29,20 @@ pub trait RealmAuth {
|
|||||||
// Get avatar
|
// Get avatar
|
||||||
// Get all userdata if you are the user
|
// Get all userdata if you are the user
|
||||||
// Server token validation
|
// Server token validation
|
||||||
|
// Logout
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
None,
|
NoError,
|
||||||
Error,
|
Error,
|
||||||
|
Unauthorized,
|
||||||
EmailTaken,
|
EmailTaken,
|
||||||
UsernameTaken,
|
UsernameTaken,
|
||||||
InvalidLoginCode,
|
InvalidLoginCode,
|
||||||
InvalidImage,
|
InvalidImage,
|
||||||
InvalidUsername,
|
InvalidUsername,
|
||||||
|
InvalidToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -47,7 +52,7 @@ pub struct AuthUser {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar: String,
|
pub avatar: String,
|
||||||
pub login_code: Option<u16>,
|
pub login_code: Option<u16>,
|
||||||
pub tokens: Option<Vec<String>>,
|
pub bigtoken: Option<String>,
|
||||||
pub google_oauth: Option<String>,
|
pub google_oauth: Option<String>,
|
||||||
pub apple_oauth: Option<String>,
|
pub apple_oauth: Option<String>,
|
||||||
pub github_oauth: Option<String>,
|
pub github_oauth: Option<String>,
|
||||||
|
|||||||
Reference in New Issue
Block a user