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 auth 4.5 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::{ReqBail, SrvErr, types::*}; 7 8pub async fn create_user( 9 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 10 Json(payload): Json<CreateUserRequest>, 11) -> Result<Json<CreateAccountResponse>, SrvErr> { 12 let key_used = { state.signup_keys.lock().await.remove(&payload.signup_key) }; 13 14 if !key_used { 15 ReqBail!("Signup key was not there"); 16 } 17 18 // todo check 19 let pub_key_bytes = match BASE64_STANDARD.decode(&payload.pub_key_b64) { 20 Ok(b) => b, 21 Err(_) => ReqBail!("Invalid base64 public key"), 22 }; 23 24 // todo check 25 let pub_key_arr: [u8; 32] = pub_key_bytes 26 .as_slice() 27 .try_into() 28 .map_err(|_| SrvErr!("Invalid pubkey length"))?; 29 let pub_key = VerifyingKey::from_bytes(&pub_key_arr) 30 .map_err(|e| SrvErr!("Invalid public key bytes", e))?; 31 32 let user_id = nanoid!(5); 33 let mut is_admin = false; 34 let mut users = state.users.lock().await; 35 if users.is_empty() { 36 is_admin = true; 37 let mut admin_id_guard = state.admin_id.lock().await; 38 admin_id_guard.replace(user_id.clone()); 39 } 40 41 users.push(User { 42 id: user_id.clone(), 43 pub_key, 44 }); 45 46 return Ok(Json(CreateAccountResponse { user_id, is_admin })); 47} 48 49pub async fn generate_signup_key(State(state): State<AppState>) -> Result<String, SrvErr> { 50 let new_signup_key = nanoid!(5); 51 let mut signup_keys = state.signup_keys.lock().await; 52 53 // Assume new_signup_key will not collide 54 signup_keys.insert(new_signup_key.clone()); 55 56 return Ok(new_signup_key); 57} 58 59pub async fn request_friend_request( 60 State(state): State<AppState>, 61 Extension(user_id): Extension<String>, 62 friend_id: String, 63) -> Result<(), SrvErr> { 64 if friend_id == user_id { 65 ReqBail!("Cannot friend yourself"); 66 } 67 68 let mut friend_requests = state.friend_requests.lock().await; 69 let link = Link::new(friend_id, user_id); 70 71 // if we remove sucessfully the link, it means a request already existed 72 // so we are making the friendship official 73 let friend_request_accepted = friend_requests.remove(&link); 74 if friend_request_accepted { 75 drop(friend_requests); 76 77 let mut pings_state = state.positions.lock().await; 78 pings_state.insert(link.clone(), RingBuffer::new(state.ring_buffer_cap)); 79 drop(pings_state); 80 81 let mut links = state.links.lock().await; 82 links.insert(link); 83 drop(links); 84 } else { 85 friend_requests.insert(link); 86 drop(friend_requests); 87 } 88 89 return Ok(()); 90} 91 92pub async fn is_friend_request_accepted( 93 State(state): State<AppState>, 94 Extension(user_id): Extension<String>, 95 friend_id: String, 96) -> Result<PlainBool, SrvErr> { 97 let link = Link::new(friend_id, user_id); 98 let links = state.links.lock().await; 99 let accepted = links.contains(&link); 100 return Ok(PlainBool(accepted)); 101} 102 103pub async fn send_pings( 104 State(state): State<AppState>, 105 Extension(user_id): Extension<String>, 106 Json(pings): Json<Vec<PingPayload>>, 107) -> Result<(), SrvErr> { 108 let links = state.links.lock().await; 109 for ping in &pings { 110 let link = Link::new(user_id.clone(), ping.receiver_id.clone()); 111 if !links.contains(&link) { 112 ReqBail!("Ping receiver is not linked to sender"); 113 } 114 } 115 drop(links); 116 117 let mut pings_state = state.positions.lock().await; 118 119 for ping in pings { 120 let link = Link::new(user_id.clone(), ping.receiver_id.clone()); 121 pings_state 122 .get_mut(&link) 123 .unwrap() 124 .add(EncryptedPing(ping.encrypted_ping)); // We assured that a ringbuffer exists because it was created when the link was created, hence the .unwrap() 125 } 126 127 return Ok(()); 128} 129 130pub async fn get_pings( 131 State(state): State<AppState>, 132 Extension(user_id): Extension<String>, 133 sender_id: String, 134) -> Result<EncryptedPingVec, SrvErr> { 135 let link = Link::new(user_id, sender_id); 136 let links = state.links.lock().await; 137 138 if !links.contains(&link) { 139 ReqBail!("No link exists between these users"); 140 } 141 drop(links); 142 143 let pings = state.positions.lock().await; 144 145 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() 146} 147 148// TODO: random idea, but use numbers instead of strings for user ids because no need to clone (but longer to read if needed) 149// ANSWER: use number, but when showing or intputing on frontend, use base64 with frontend translation