-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
350 additions
and
27 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,14 +5,29 @@ authors = ["Stan Drozd <[email protected]>"] | |
edition = "2018" | ||
|
||
[workspace] | ||
members = ["althea_kernel_interface", "settings", "clu", "exit_db", "antenna_forwarding_client", "antenna_forwarding_protocol", "auto_bridge","rita_common","rita_exit","rita_client", "rita_bin", "test_runner", "integration_tests"] | ||
members = [ | ||
"althea_kernel_interface", | ||
"settings", | ||
"clu", | ||
"exit_db", | ||
"antenna_forwarding_client", | ||
"antenna_forwarding_protocol", | ||
"auto_bridge", | ||
"rita_common", | ||
"rita_exit", | ||
"rita_client", | ||
"rita_client_registration", | ||
"rita_bin", | ||
"test_runner", | ||
"integration_tests", | ||
] | ||
|
||
# Production relase profile, every trick is used to reduce binary size | ||
[profile.release] | ||
opt-level = "z" | ||
strip = true | ||
lto = true | ||
codegen-units=1 | ||
codegen-units = 1 | ||
incremental = false | ||
|
||
# testrunner should be fast to execute but also to compile | ||
|
@@ -23,4 +38,4 @@ inherits = "dev" | |
opt-level = 2 | ||
|
||
[workspace.dependencies] | ||
deep_space = {version = "2", features = ["althea"], default-features=false} | ||
deep_space = { version = "2", features = ["althea"], default-features = false } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "rita_client_registration" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
lazy_static = "1.4" | ||
althea_types = { path = "../althea_types" } | ||
log = { version = "0.4", features = ["release_max_level_info"] } | ||
serde = "1.0" | ||
clarity = "1.2" | ||
phonenumber = "0.3" | ||
awc = "3.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::net::IpAddr; | ||
|
||
use althea_types::{Identity, WgKey}; | ||
use clarity::Address; | ||
|
||
pub fn get_all_regsitered_clients() -> Vec<Identity> { | ||
unimplemented!() | ||
} | ||
|
||
pub fn get_registered_client_using_wgkey(_key: WgKey) -> Option<Identity> { | ||
unimplemented!() | ||
} | ||
|
||
pub fn get_registered_client_using_ethkey(_key: Address) -> Option<Identity> { | ||
unimplemented!() | ||
} | ||
|
||
pub fn get_registered_client_using_meship(_ip: IpAddr) -> Option<Identity> { | ||
unimplemented!() | ||
} | ||
|
||
pub fn get_clients_exit_cluster_list(_key: WgKey) -> Vec<Identity> { | ||
unimplemented!() | ||
} | ||
|
||
pub fn add_client_to_registered_list(_c: Identity) { | ||
unimplemented!() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
#![deny(unused_crate_dependencies)] | ||
use std::{ | ||
collections::HashMap, | ||
sync::{Arc, RwLock}, | ||
}; | ||
|
||
use althea_types::{ExitClientIdentity, WgKey}; | ||
use phonenumber::PhoneNumber; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::client_db::{ | ||
add_client_to_registered_list, get_registered_client_using_ethkey, | ||
get_registered_client_using_meship, get_registered_client_using_wgkey, | ||
}; | ||
|
||
#[macro_use] | ||
extern crate log; | ||
#[macro_use] | ||
extern crate lazy_static; | ||
|
||
pub mod client_db; | ||
|
||
lazy_static! { | ||
/// A map that stores number of texts sent to a client during registration | ||
static ref TEXTS_SENT: Arc<RwLock<HashMap<WgKey, u8>>> = Arc::new(RwLock::new(HashMap::new())); | ||
} | ||
|
||
/// Return struct from check_text and Send Text. Verified indicates status from api http req, | ||
/// bad phone number is an error parsing clients phone number | ||
/// Internal server error is an error while querying api endpoint | ||
enum TextApiError { | ||
BadPhoneNumber, | ||
InternalServerError { error: String }, | ||
} | ||
|
||
/// Return struct from Registration server to exit | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub enum ExitSignupReturn { | ||
RegistrationOk, | ||
PendingRegistration, | ||
BadPhoneNumber, | ||
InternalServerError { e: String }, | ||
} | ||
// Lazy static setters and getters | ||
fn increment_texts_sent(key: WgKey) { | ||
let lock = &mut *TEXTS_SENT.write().unwrap(); | ||
let txt_sent = lock.get_mut(&key); | ||
if let Some(val) = txt_sent { | ||
*val += 1; | ||
} else { | ||
lock.insert(key, 1); | ||
} | ||
} | ||
|
||
fn reset_texts_sent(key: WgKey) { | ||
TEXTS_SENT.write().unwrap().remove(&key); | ||
} | ||
|
||
fn get_texts_sent(key: WgKey) -> u8 { | ||
*TEXTS_SENT.read().unwrap().get(&key).unwrap_or(&0u8) | ||
} | ||
|
||
#[derive(Serialize)] | ||
pub struct SmsCheck { | ||
api_key: String, | ||
verification_code: String, | ||
phone_number: String, | ||
country_code: String, | ||
} | ||
|
||
#[derive(Serialize)] | ||
pub struct SmsRequest { | ||
api_key: String, | ||
via: String, | ||
phone_number: String, | ||
country_code: String, | ||
} | ||
|
||
/// True if there is any client with the same eth address, wg key, or ip address already registered | ||
pub fn client_conflict(client: &ExitClientIdentity) -> bool { | ||
// we can't possibly have a conflict if we have exactly this client already | ||
// since client exists checks all major details this is safe and will return false | ||
// if it's not exactly the same client | ||
if client_exists(client) { | ||
return false; | ||
} | ||
trace!("Checking if client exists"); | ||
let ip = client.global.mesh_ip; | ||
let wg = client.global.wg_public_key; | ||
let key = client.global.eth_address; | ||
|
||
let ip_exists = get_registered_client_using_meship(ip).is_some(); | ||
let wg_exists = get_registered_client_using_wgkey(wg).is_some(); | ||
let eth_exists = get_registered_client_using_ethkey(key).is_some(); | ||
|
||
info!( | ||
"Signup conflict ip {} eth {} wg {}", | ||
ip_exists, eth_exists, wg_exists | ||
); | ||
ip_exists || eth_exists || wg_exists | ||
} | ||
|
||
fn client_exists(client: &ExitClientIdentity) -> bool { | ||
trace!("Checking if client exists"); | ||
let c_id = get_registered_client_using_wgkey(client.global.wg_public_key); | ||
match c_id { | ||
Some(a) => client.global == a, | ||
None => false, | ||
} | ||
} | ||
|
||
/// Handles the minutia of phone registration states | ||
pub async fn handle_sms_registration( | ||
client: ExitClientIdentity, | ||
api_key: String, | ||
magic_number: Option<String>, | ||
) -> ExitSignupReturn { | ||
info!( | ||
"Handling phone registration for {}", | ||
client.global.wg_public_key | ||
); | ||
|
||
// Get magic phone number | ||
let magic_phone_number = magic_number; | ||
|
||
let text_num = get_texts_sent(client.global.wg_public_key); | ||
let sent_more_than_allowed_texts = text_num > 10; | ||
|
||
match ( | ||
client.reg_details.phone.clone(), | ||
client.reg_details.phone_code.clone(), | ||
sent_more_than_allowed_texts, | ||
) { | ||
// all texts exhausted, but they can still submit the correct code | ||
(Some(number), Some(code), true) => { | ||
let is_magic = | ||
magic_phone_number.is_some() && magic_phone_number.unwrap() == number.clone(); | ||
let check_text = match check_text(number.clone(), code, api_key).await { | ||
Ok(a) => a, | ||
Err(e) => return return_api_error(e), | ||
}; | ||
let result = is_magic || check_text; | ||
if result { | ||
info!( | ||
"Phone registration complete for {}", | ||
client.global.wg_public_key | ||
); | ||
add_client_to_registered_list(client.global); | ||
reset_texts_sent(client.global.wg_public_key); | ||
ExitSignupReturn::RegistrationOk | ||
} else { | ||
ExitSignupReturn::PendingRegistration | ||
} | ||
} | ||
// user has exhausted attempts but is still not submitting code | ||
(Some(_number), None, true) => ExitSignupReturn::PendingRegistration, | ||
// user has attempts remaining and is requesting the code be resent | ||
(Some(number), None, false) => { | ||
if let Err(e) = send_text(number, api_key).await { | ||
return return_api_error(e); | ||
} | ||
increment_texts_sent(client.global.wg_public_key); | ||
ExitSignupReturn::PendingRegistration | ||
} | ||
// user has attempts remaining and is submitting a code | ||
(Some(number), Some(code), false) => { | ||
let is_magic = | ||
magic_phone_number.is_some() && magic_phone_number.unwrap() == number.clone(); | ||
let check_text = match check_text(number.clone(), code, api_key).await { | ||
Ok(a) => a, | ||
Err(e) => return return_api_error(e), | ||
}; | ||
|
||
let result = is_magic | check_text; | ||
trace!("Check text returned {}", result); | ||
if result { | ||
info!( | ||
"Phone registration complete for {}", | ||
client.global.wg_public_key | ||
); | ||
add_client_to_registered_list(client.global); | ||
reset_texts_sent(client.global.wg_public_key); | ||
ExitSignupReturn::RegistrationOk | ||
} else { | ||
ExitSignupReturn::PendingRegistration | ||
} | ||
} | ||
// user did not submit a phonenumber | ||
(None, _, _) => ExitSignupReturn::BadPhoneNumber, | ||
} | ||
} | ||
|
||
fn return_api_error(e: TextApiError) -> ExitSignupReturn { | ||
match e { | ||
TextApiError::BadPhoneNumber => ExitSignupReturn::BadPhoneNumber, | ||
TextApiError::InternalServerError { error } => { | ||
ExitSignupReturn::InternalServerError { e: error } | ||
} | ||
} | ||
} | ||
|
||
/// Posts to the validation endpoint with the code, will return success if the code | ||
/// is the same as the one sent to the user | ||
async fn check_text(number: String, code: String, api_key: String) -> Result<bool, TextApiError> { | ||
trace!("About to check text message status for {}", number); | ||
let number: PhoneNumber = match number.parse() { | ||
Ok(number) => number, | ||
Err(e) => { | ||
error!("Phone parse error: {}", e); | ||
return Err(TextApiError::BadPhoneNumber); | ||
} | ||
}; | ||
let url = "https://api.authy.com/protected/json/phones/verification/check"; | ||
|
||
let client = awc::Client::default(); | ||
let response = match client | ||
.get(url) | ||
.send_form(&SmsCheck { | ||
api_key, | ||
verification_code: code, | ||
phone_number: number.national().to_string(), | ||
country_code: number.code().value().to_string(), | ||
}) | ||
.await | ||
{ | ||
Ok(a) => a, | ||
Err(e) => { | ||
return Err(TextApiError::InternalServerError { | ||
error: e.to_string(), | ||
}) | ||
} | ||
}; | ||
|
||
trace!("Got {} back from check text", response.status()); | ||
Ok(response.status().is_success()) | ||
} | ||
|
||
/// Sends the authy verification text by hitting the api endpoint | ||
async fn send_text(number: String, api_key: String) -> Result<(), TextApiError> { | ||
info!("Sending message for {}", number); | ||
let url = "https://api.authy.com/protected/json/phones/verification/start"; | ||
let number: PhoneNumber = match number.parse() { | ||
Ok(number) => number, | ||
Err(e) => { | ||
error!("Parse phone number error {}", e); | ||
return Err(TextApiError::BadPhoneNumber); | ||
} | ||
}; | ||
|
||
let client = awc::Client::default(); | ||
match client | ||
.post(url) | ||
.send_form(&SmsRequest { | ||
api_key, | ||
via: "sms".to_string(), | ||
phone_number: number.national().to_string(), | ||
country_code: number.code().value().to_string(), | ||
}) | ||
.await | ||
{ | ||
Ok(_a) => Ok(()), | ||
Err(e) => { | ||
error!("Send text error! {}", e); | ||
Err(TextApiError::InternalServerError { | ||
error: e.to_string(), | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.