use activitystreams::base::AnyBase; use sled::{Db, Tree}; use std::{fmt, sync::Arc}; use url::Url; use uuid::Uuid; pub mod actions; mod extensions; mod results; pub(crate) use actions::ingest; use extensions::{ ExtendedApplication, ExtendedNote, ExtendedPerson, ManuallyApprovesFollowers, PublicKey, PublicKeyInner, Sensitive, }; pub trait ApubIds { fn gen_id(&self) -> Option; fn public_key(&self, id: &Url) -> Option; fn following(&self, id: &Url) -> Option; fn followers(&self, id: &Url) -> Option; fn inbox(&self, id: &Url) -> Option; fn outbox(&self, id: &Url) -> Option; fn shared_inbox(&self) -> Url; } #[derive(Debug, thiserror::Error)] pub enum StoreError { #[error("{0}")] Sled(#[from] sled::Error), #[error("{0}")] Transaction(#[from] sled::transaction::TransactionError), #[error("{0}")] Json(#[from] serde_json::Error), #[error("Error generating keys")] Key, } #[derive(Clone)] pub struct Store { apub_id: Tree, id_apub: Tree, objects: Tree, deleted: Tree, keys: Tree, profile_key: Tree, key_profile: Tree, server_key: Tree, key_server: Tree, endpoints: Tree, profile_endpoint: Tree, server_endpoint: Tree, info: Arc, } #[derive(Clone, Debug)] pub enum StoredRecords { Server(Uuid), Report(Uuid), Profile(Uuid), Submission(Uuid), Comment(Uuid), React(Uuid), File(Uuid), Block(Uuid), Follow(Uuid), FollowRequest(Uuid), ServerFollow(Uuid), ServerFollowRequest(Uuid), } fn url_from_ivec(ivec: sled::IVec) -> Option { let s = String::from_utf8_lossy(&ivec); s.parse::().ok() } fn record_from_ivec(ivec: sled::IVec) -> Option { StoredRecords::from_slice(&ivec) .map_err(|e| log::warn!("{}", e)) .ok() } impl StoredRecords { fn from_slice(slice: &[u8]) -> Result { String::from_utf8_lossy(slice).parse::() } pub fn server(&self) -> Option { match self { Self::Server(id) => Some(*id), _ => None, } } pub fn profile(&self) -> Option { match self { Self::Profile(id) => Some(*id), _ => None, } } pub fn submission(&self) -> Option { match self { Self::Submission(id) => Some(*id), _ => None, } } pub fn comment(&self) -> Option { match self { Self::Comment(id) => Some(*id), _ => None, } } pub fn react(&self) -> Option { match self { Self::React(id) => Some(*id), _ => None, } } pub fn file(&self) -> Option { match self { Self::File(id) => Some(*id), _ => None, } } pub fn block(&self) -> Option { match self { Self::Block(id) => Some(*id), _ => None, } } pub fn follow(&self) -> Option { match self { Self::Follow(id) => Some(*id), _ => None, } } pub fn follow_request(&self) -> Option { match self { Self::FollowRequest(id) => Some(*id), _ => None, } } pub fn server_follow(&self) -> Option { match self { Self::ServerFollow(id) => Some(*id), _ => None, } } pub fn server_follow_request(&self) -> Option { match self { Self::ServerFollowRequest(id) => Some(*id), _ => None, } } } #[derive(Debug, thiserror::Error)] #[error("Error generating key")] struct KeyError; #[derive(serde::Deserialize, serde::Serialize)] struct Keys { public: String, private: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] struct Endpoints { inbox: Url, outbox: Url, following: Option, followers: Option, shared_inbox: Option, public_key: Url, } impl Endpoints { fn inbox(&self) -> &Url { self.shared_inbox.as_ref().unwrap_or(&self.inbox) } } impl Store { pub(crate) fn build( ids: impl ApubIds + Send + Sync + 'static, db: &Db, ) -> Result { Ok(Store { id_apub: db.open_tree("/profiles/apub/id-apub")?, apub_id: db.open_tree("/profiles/apub/apub-id")?, objects: db.open_tree("/profiles/apub/objects")?, deleted: db.open_tree("/profiles/apub/deleted")?, keys: db.open_tree("/profiles/apub/keys")?, profile_key: db.open_tree("/profiles/apub/profile/key")?, key_profile: db.open_tree("/profiles/apub/key/profile")?, server_key: db.open_tree("/profiles/apub/server/key")?, key_server: db.open_tree("/profiles/apub/key/server")?, endpoints: db.open_tree("/profiles/apub/endpoints")?, profile_endpoint: db.open_tree("/profiles/apub/profile/endpoint")?, server_endpoint: db.open_tree("/profiles/apub/server/endpoint")?, info: Arc::new(ids), }) } fn endpoints_for_server(&self, server_id: Uuid) -> Result, StoreError> { let opt = self .server_endpoint .get(server_endpoint_id(server_id))? .and_then(url_from_ivec); if let Some(server_id) = opt { if let Some(ivec) = self.endpoints.get(server_id.as_str())? { let endpoints: Endpoints = serde_json::from_slice(&ivec)?; return Ok(Some(endpoints)); } } Ok(None) } pub fn key_for_server(&self, server_id: Uuid) -> Result, StoreError> { self.server_key .get(server_key_id(server_id)) .map(|opt| opt.and_then(url_from_ivec)) .map_err(StoreError::from) } pub(super) fn server_for_key(&self, key_id: &Url) -> Result, StoreError> { self.key_server .get(key_id.as_str()) .map(|opt| opt.and_then(uuid_from_ivec)) .map_err(StoreError::from) } fn endpoints_for_profile(&self, profile_id: Uuid) -> Result, StoreError> { let opt = self .profile_endpoint .get(profile_endpoint_id(profile_id))? .and_then(url_from_ivec); if let Some(profile_id) = opt { if let Some(ivec) = self.endpoints.get(profile_id.as_str())? { let endpoints: Endpoints = serde_json::from_slice(&ivec)?; return Ok(Some(endpoints)); } } Ok(None) } pub fn key_for_profile(&self, profile_id: Uuid) -> Result, StoreError> { self.profile_key .get(profile_key_id(profile_id)) .map(|opt| opt.and_then(url_from_ivec)) .map_err(StoreError::from) } pub(super) fn profile_for_key(&self, key_id: &Url) -> Result, StoreError> { self.key_profile .get(key_id.as_str()) .map(|opt| opt.and_then(uuid_from_ivec)) .map_err(StoreError::from) } pub fn public_key_for_id(&self, key_id: &Url) -> Result, StoreError> { if let Some(ivec) = self.keys.get(key_id.as_str())? { let keys: Keys = serde_json::from_slice(&ivec)?; return Ok(Some(keys.public)); } Ok(None) } pub fn private_key_for_id(&self, key_id: &Url) -> Result, StoreError> { if let Some(ivec) = self.keys.get(key_id.as_str())? { let keys: Keys = serde_json::from_slice(&ivec)?; return Ok(keys.private); } Ok(None) } fn store_server_endpoints( &self, server_id: Uuid, apub_id: &Url, endpoints: Endpoints, ) -> Result<(), StoreError> { let endpoints_vec = serde_json::to_vec(&endpoints)?; self.endpoints.insert(apub_id.as_str(), endpoints_vec)?; self.server_endpoint .insert(server_endpoint_id(server_id), apub_id.as_str())?; Ok(()) } fn store_profile_endpoints( &self, profile_id: Uuid, apub_id: &Url, endpoints: Endpoints, ) -> Result<(), StoreError> { let endpoints_vec = serde_json::to_vec(&endpoints)?; self.endpoints.insert(apub_id.as_str(), endpoints_vec)?; self.profile_endpoint .insert(profile_endpoint_id(profile_id), apub_id.as_str())?; Ok(()) } fn gen_server_keys(&self, server_id: Uuid, key_id: &Url) -> Result { let keys = Keys::generate()?; let keys_vec = serde_json::to_vec(&keys)?; self.keys.insert(key_id.as_str(), keys_vec)?; self.server_key .insert(server_key_id(server_id), key_id.as_str())?; self.key_server .insert(key_id.as_str(), server_id.as_bytes())?; Ok(keys.public) } fn gen_profile_keys(&self, profile_id: Uuid, key_id: &Url) -> Result { let keys = Keys::generate()?; let keys_vec = serde_json::to_vec(&keys)?; self.keys.insert(key_id.as_str(), keys_vec)?; self.profile_key .insert(profile_key_id(profile_id), key_id.as_str())?; self.key_profile .insert(key_id.as_str(), profile_id.as_bytes())?; Ok(keys.public) } fn store_server_public_key( &self, server_id: Uuid, key_id: &Url, public_key: &str, ) -> Result<(), StoreError> { let keys = Keys::from_public(public_key)?; let keys_vec = serde_json::to_vec(&keys)?; self.keys.insert(key_id.as_str(), keys_vec)?; self.server_key .insert(server_key_id(server_id), key_id.as_str())?; self.key_server .insert(key_id.as_str(), server_id.as_bytes())?; Ok(()) } fn store_profile_public_key( &self, profile_id: Uuid, key_id: &Url, public_key: &str, ) -> Result<(), StoreError> { let keys = Keys::from_public(public_key)?; let keys_vec = serde_json::to_vec(&keys)?; self.keys.insert(key_id.as_str(), keys_vec)?; self.profile_key .insert(profile_key_id(profile_id), key_id.as_str())?; self.key_profile .insert(key_id.as_str(), profile_id.as_bytes())?; Ok(()) } pub(super) fn deleted(&self, id: &Url) -> Result { Ok(self.deleted.get(id.as_str())?.is_some()) } fn delete_object(&self, id: &Url) -> Result<(), StoreError> { self.deleted.insert(id.as_str(), id.as_str())?; Ok(()) } pub(super) fn object(&self, id: &Url) -> Result, StoreError> { if let Some(ivec) = self.objects.get(id.as_str())? { let any_base: AnyBase = serde_json::from_slice(&ivec)?; return Ok(Some(any_base)); } Ok(None) } pub(super) fn store_object(&self, any_base: &AnyBase) -> Result<(), StoreError> { if let Some(id) = any_base.id() { let obj_vec = serde_json::to_vec(any_base)?; self.objects.insert(id.as_str(), obj_vec)?; } Ok(()) } fn apub_for_server_follow_request(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::ServerFollowRequest(id)) } fn server_follow_request(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::ServerFollowRequest(id)) } fn apub_for_server_follow(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::ServerFollow(id)) } fn server_follow(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::ServerFollow(id)) } pub fn apub_for_server(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Server(id)) } fn server(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Server(id)) } fn apub_for_submission(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Submission(id)) } fn submission(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Submission(id)) } pub fn apub_for_profile(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Profile(id)) } fn profile(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Profile(id)) } fn report(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Report(id)) } fn apub_for_comment(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Comment(id)) } fn comment(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Comment(id)) } fn apub_for_react(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::React(id)) } fn react(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::React(id)) } fn apub_for_file(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::File(id)) } pub(super) fn file(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::File(id)) } fn apub_for_follow(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Follow(id)) } fn follow(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Follow(id)) } fn apub_for_follow_request(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::FollowRequest(id)) } fn follow_request(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::FollowRequest(id)) } fn apub_for_block(&self, id: Uuid) -> Result, StoreError> { self.apub_for_id(StoredRecords::Block(id)) } fn block(&self, apub_id: &Url, id: Uuid) -> Result<(), StoreError> { self.establish(apub_id, StoredRecords::Block(id)) } fn establish(&self, apub_id: &Url, id: StoredRecords) -> Result<(), StoreError> { self.apub_id .insert(apub_id.as_str(), id.to_string().as_bytes())?; self.id_apub .insert(id.to_string(), apub_id.as_str().as_bytes())?; Ok(()) } pub(super) fn id_for_apub(&self, apub_id: &Url) -> Result, StoreError> { Ok(self .apub_id .get(apub_id.as_str())? .and_then(record_from_ivec)) } fn apub_for_id(&self, id: StoredRecords) -> Result, StoreError> { Ok(self.id_apub.get(id.to_string())?.and_then(url_from_ivec)) } } impl Keys { fn from_public(public: &str) -> Result { use rsa::RSAPublicKey; use rsa_pem::KeyExt; RSAPublicKey::from_pem_pkcs8(public).map_err(|_| KeyError)?; Ok(Keys { public: public.to_owned(), private: None, }) } fn generate() -> Result { use rsa::RSAPrivateKey; use rsa_pem::KeyExt; let mut rng = rand::thread_rng(); let private_key = RSAPrivateKey::new(&mut rng, 2048).map_err(|_| KeyError)?; let private = private_key.to_pem_pkcs8().map_err(|_| KeyError)?; let public_key = private_key.to_public_key(); let public = public_key.to_pem_pkcs8().map_err(|_| KeyError)?; Ok(Keys { public, private: Some(private), }) } } fn profile_endpoint_id(profile_id: Uuid) -> String { format!("/profile/{}/endpoint-id", profile_id) } fn server_endpoint_id(server_id: Uuid) -> String { format!("/server/{}/endpoint-id", server_id) } fn profile_key_id(profile_id: Uuid) -> String { format!("/profile/{}/key-id", profile_id) } fn server_key_id(server_id: Uuid) -> String { format!("/server/{}/key-id", server_id) } impl fmt::Debug for Store { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Store") .field("relationship", &"Tree") .field("apub_id", &"Tree") .field("id_apub", &"Tree") .field("objects", &"Tree") .field("keys", &"Tree") .field("profile_key", &"Tree") .field("server_key", &"Tree") .field("endpoints", &"Tree") .field("profile_endpoint", &"Tree") .field("server_endpoint", &"Tree") .field("info", &"Rc") .finish() } } #[derive(Debug, thiserror::Error)] #[error("Failed to parse StoredRecord: {0}")] pub struct StoredRecordsError(String); impl std::str::FromStr for StoredRecords { type Err = StoredRecordsError; fn from_str(s: &str) -> Result { let split_pos = s .find(':') .ok_or(StoredRecordsError(format!("no ':' in {}", s)))?; let (kind, uuid) = s.split_at(split_pos); let uuid = uuid.trim_start_matches(':'); let id = uuid .parse::() .map_err(|e| StoredRecordsError(e.to_string()))?; let record = match kind { "server" => StoredRecords::Server(id), "report" => StoredRecords::Report(id), "profile" => StoredRecords::Profile(id), "submission" => StoredRecords::Submission(id), "comment" => StoredRecords::Comment(id), "react" => StoredRecords::React(id), "file" => StoredRecords::File(id), "block" => StoredRecords::Block(id), "follow" => StoredRecords::Follow(id), "follow-request" => StoredRecords::FollowRequest(id), "server-follow" => StoredRecords::ServerFollow(id), "server-follow-request" => StoredRecords::ServerFollowRequest(id), other => return Err(StoredRecordsError(format!("record kind: {}", other))), }; Ok(record) } } impl fmt::Display for StoredRecords { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StoredRecords::Server(id) => write!(f, "server:{}", id), StoredRecords::Report(id) => write!(f, "report:{}", id), StoredRecords::Profile(id) => write!(f, "profile:{}", id), StoredRecords::Submission(id) => write!(f, "submission:{}", id), StoredRecords::Comment(id) => write!(f, "comment:{}", id), StoredRecords::React(id) => write!(f, "react:{}", id), StoredRecords::File(id) => write!(f, "file:{}", id), StoredRecords::Block(id) => write!(f, "block:{}", id), StoredRecords::Follow(id) => write!(f, "follow:{}", id), StoredRecords::FollowRequest(id) => write!(f, "follow-request:{}", id), StoredRecords::ServerFollow(id) => write!(f, "server-follow:{}", id), StoredRecords::ServerFollowRequest(id) => write!(f, "server-follow-request:{}", id), } } } impl From for StoreError { fn from(_: KeyError) -> Self { StoreError::Key } } fn uuid_from_ivec(ivec: sled::IVec) -> Option { Uuid::from_slice(&ivec).ok() }