Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated
at ui 4.7 kB view raw
1use axum::{Extension, Json, extract::State, response::Result}; 2use base64::{Engine, prelude::BASE64_STANDARD}; 3use ed25519_dalek::VerifyingKey; 4use nanoid::nanoid; 5 6use crate::types::*; 7 8macro_rules! my_err { 9 ($msg:expr) => { 10 Err(MyErr($msg)) 11 }; 12} 13 14pub async fn create_user( 15 State(state): State<AppState>, // TODO: some time ago, I change this (and all other handlers) to State(state): State<Arc<AppState>> no idea if I actually need it 16 Json(payload): Json<CreateUserRequest>, 17) -> Result<Json<CreateAccountResponse>, MyErr> { 18 let key_used = { state.signup_keys.lock().await.remove(&payload.signup_key) }; 19 20 if !key_used { 21 return my_err!("Signup key was not there"); 22 } 23 24 // todo check 25 let pub_key_bytes = match BASE64_STANDARD.decode(&payload.pub_key_b64) { 26 Ok(b) => b, 27 Err(_) => return my_err!("Invalid base64 public key"), 28 }; 29 30 // todo check 31 let pub_key = match VerifyingKey::from_bytes( 32 &pub_key_bytes 33 .try_into() 34 .map_err(|_| MyErr("Invalid pubkey length".into()))?, 35 ) { 36 Ok(pk) => pk, 37 Err(_) => return my_err!("Invalid public key bytes"), 38 }; 39 40 let user_id = nanoid!(5); 41 let mut is_admin = false; 42 let mut users = state.users.lock().await; 43 if users.is_empty() { 44 is_admin = true; 45 let mut admin_id_guard = state.admin_id.lock().await; 46 admin_id_guard.replace(user_id.clone()); 47 } 48 49 users.push(User { 50 id: user_id.clone(), 51 pub_key, 52 }); 53 54 return Ok(Json(CreateAccountResponse { user_id, is_admin })); 55} 56 57pub async fn generate_signup_key(State(state): State<AppState>) -> Result<String, MyErr> { 58 let new_signup_key = nanoid!(5); 59 let mut signup_keys = state.signup_keys.lock().await; 60 61 // Assume new_signup_key will not collide 62 signup_keys.insert(new_signup_key.clone()); 63 64 return Ok(new_signup_key); 65} 66 67pub async fn create_friend_request( 68 State(state): State<AppState>, 69 Extension(user_id): Extension<String>, 70 accepter_id: String, 71) -> Result<(), MyErr> { 72 if accepter_id == user_id { 73 return my_err!("Cannot friend yourself"); 74 } 75 76 let mut friend_requests = state.friend_requests.lock().await; 77 let link = Link::new(accepter_id, user_id); 78 if friend_requests.contains(&link) { 79 return my_err!("Friend request already exists"); 80 } 81 friend_requests.insert(link); 82 return Ok(()); 83} 84 85pub async fn accept_friend_request( 86 State(state): State<AppState>, 87 Extension(user_id): Extension<String>, 88 sender_id: String, 89) -> Result<(), MyErr> { 90 let link = Link::new(user_id, sender_id); 91 92 let friend_request_accepted = { state.friend_requests.lock().await.remove(&link) }; 93 94 if !friend_request_accepted { 95 return my_err!("Friend request not found"); 96 } 97 98 let mut pings_state = state.positions.lock().await; 99 pings_state.insert(link.clone(), RingBuffer::new(state.ring_buffer_cap)); 100 drop(pings_state); 101 102 let mut links = state.links.lock().await; 103 links.insert(link); 104 105 return Ok(()); 106} 107 108pub async fn is_friend_request_accepted( 109 State(state): State<AppState>, 110 Extension(user_id): Extension<String>, 111 friend_id: String, 112) -> Result<PlainBool, MyErr> { 113 let link = Link::new(friend_id, user_id); 114 let links = state.links.lock().await; 115 let accepted = links.contains(&link); 116 return Ok(PlainBool(accepted)); 117} 118 119pub async fn send_pings( 120 State(state): State<AppState>, 121 Extension(user_id): Extension<String>, 122 Json(pings): Json<Vec<PingPayload>>, 123) -> Result<(), MyErr> { 124 let links = state.links.lock().await; 125 for ping in &pings { 126 let link = Link::new(user_id.clone(), ping.receiver_id.clone()); 127 if !links.contains(&link) { 128 return my_err!("Ping receiver is not linked to sender"); 129 } 130 } 131 drop(links); 132 133 let mut pings_state = state.positions.lock().await; 134 135 for ping in pings { 136 let link = Link::new(user_id.clone(), ping.receiver_id.clone()); 137 pings_state 138 .get_mut(&link) 139 .unwrap() 140 .add(EncryptedPing(ping.encrypted_ping)); // We assured that a ringbuffer exists because it was created when the link was created, hence the .unwrap() 141 } 142 143 return Ok(()); 144} 145 146pub async fn get_pings( 147 State(state): State<AppState>, 148 Extension(user_id): Extension<String>, 149 sender_id: String, 150) -> Result<EncryptedPingVec, MyErr> { 151 let link = Link::new(user_id, sender_id); 152 let links = state.links.lock().await; 153 154 if !links.contains(&link) { 155 return my_err!("No link exists between these users"); 156 } 157 drop(links); 158 159 let pings = state.positions.lock().await; 160 161 return Ok(EncryptedPingVec(pings.get(&link).unwrap().flatten())); // We assured that a ringbuffer exists because it was created when the link was created, hence the .unwrap() 162} 163 164// TODO: random idea, but use numbers instead of strings for user ids because no need to clone (but longer to read if needed) 165// ANSWER: use number, but when showing or intputing on frontend, use base64 with frontend translation