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.1 kB view raw
1use axum::{ 2 http::StatusCode, 3 response::{IntoResponse, Response}, 4}; 5use ed25519_dalek::VerifyingKey; 6use serde::{Deserialize, Serialize}; 7use std::{ 8 collections::{HashMap, HashSet}, 9 sync::Arc, 10}; 11use tokio::sync::Mutex; 12 13// doing everything in memory for now 14#[derive(Clone)] 15pub struct AppState { 16 pub users: Arc<Mutex<Vec<User>>>, 17 pub signup_keys: Arc<Mutex<HashSet<String>>>, 18 pub friend_requests: Arc<Mutex<HashSet<Link>>>, 19 pub links: Arc<Mutex<HashSet<Link>>>, 20 pub positions: Arc<Mutex<HashMap<Link, RingBuffer>>>, 21 pub admin_id: Arc<Mutex<Option<String>>>, 22 pub ring_buffer_cap: usize, 23} 24 25#[derive(Debug, Deserialize, Clone)] 26pub struct AuthData { 27 pub user_id: String, 28 pub signature: String, 29} 30 31pub struct RingBuffer { 32 pub ring: Box<[Option<EncryptedPing>]>, 33 pub idx: usize, 34} 35 36#[derive(Clone, Serialize)] 37pub struct EncryptedPing(pub String); 38 39// represents a ring buffer for a directed friend connection (ex.: user1 sending to user2, which in that case it's only user1's positions) 40impl RingBuffer { 41 pub fn new(capacity: usize) -> Self { 42 return Self { 43 ring: vec![None; capacity].into_boxed_slice(), 44 idx: 0, 45 }; 46 } 47 48 pub fn add(&mut self, p: EncryptedPing) { 49 self.idx = (self.idx + 1) % self.ring.len(); 50 self.ring[self.idx] = Some(p); 51 } 52 53 /// Returns a `Vec<String>` of all the `encrypted_ping` values in the ring buffer, 54 /// starting from `self.idx` and iterating **backwards**, wrapping around to the end, 55 /// skipping `None` entries. 56 /// 57 /// # Notes 58 /// - The first element in the returned vector corresponds to the current index (`self.idx`). 59 /// - `None` entries are ignored. 60 pub fn flatten(&self) -> Vec<EncryptedPing> { 61 let len = self.ring.len(); 62 63 let mut result = Vec::with_capacity(len); 64 65 for i in 0..len { 66 let temp = (self.idx + len - i) % len; 67 let position = &self.ring[temp]; 68 match position { 69 Some(p) => result.push(p.clone()), 70 None => continue, 71 } 72 } 73 74 return result; 75 } 76} 77 78#[derive(serde::Deserialize)] 79pub struct CreateUserRequest { 80 pub signup_key: String, 81 pub pub_key_b64: String, // base64-encoded public key 82} 83 84#[derive(Debug, PartialEq, Eq)] 85pub struct User { 86 pub id: String, 87 pub pub_key: VerifyingKey, 88} 89 90#[derive(Debug, Clone, PartialEq, Eq, Hash)] 91pub struct Link(String, String); 92 93impl Link { 94 pub fn new(a: String, b: String) -> Self { 95 if a < b { Link(a, b) } else { Link(b, a) } // normalize order 96 } 97} 98 99#[derive(Serialize)] 100pub struct CreateAccountResponse { 101 pub user_id: String, 102 pub is_admin: bool, 103} 104 105#[derive(Deserialize)] 106pub struct PingPayload { 107 pub receiver_id: String, 108 pub encrypted_ping: String, 109} 110 111pub struct PlainBool(pub bool); 112 113impl IntoResponse for PlainBool { 114 fn into_response(self) -> Response { 115 self.0.to_string().into_response() 116 } 117} 118 119#[derive(Serialize)] 120pub struct EncryptedPingVec(pub Vec<EncryptedPing>); 121 122impl IntoResponse for EncryptedPingVec { 123 fn into_response(self) -> Response { 124 axum::Json(self.0).into_response() 125 } 126} 127 128#[derive(Debug)] 129pub struct SrvErr { 130 pub msg: String, 131 pub cause: Option<String>, 132} 133 134impl IntoResponse for SrvErr { 135 fn into_response(self) -> Response { 136 // Log once here (this runs only for real errors) 137 match &self.cause { 138 Some(c) => eprintln!("[ERR] {} | cause: {}", self.msg, c), 139 None => eprintln!("[ERR] {}", self.msg), 140 } 141 142 let body = if cfg!(debug_assertions) { 143 match &self.cause { 144 Some(c) => format!("{} | cause: {}", self.msg, c), 145 None => self.msg.clone(), 146 } 147 } else { 148 self.msg.clone() 149 }; 150 151 (StatusCode::INTERNAL_SERVER_ERROR, body).into_response() 152 } 153} 154 155/// Central policy: what gets logged, what gets returned. 156pub fn mk_srv_err(msg: impl Into<String>, cause: Option<String>) -> SrvErr { 157 SrvErr { 158 msg: msg.into(), 159 cause, 160 } 161} 162 163#[macro_export] 164macro_rules! SrvErr { 165 ($msg:expr) => { 166 $crate::mk_srv_err($msg, None) 167 }; 168 ($msg:expr, $err:expr) => { 169 $crate::mk_srv_err($msg, Some(format!("{:?}", $err))) 170 }; 171} 172 173#[macro_export] 174macro_rules! ReqBail { 175 ($msg:expr) => {{ 176 return Err($crate::SrvErr!($msg)); 177 }}; 178 ($msg:expr, $err:expr) => {{ 179 return Err($crate::SrvErr!($msg, $err)); 180 }}; 181}