relay/src/db.rs

855 lines
27 KiB
Rust
Raw Normal View History

2022-01-17 22:54:45 +00:00
use crate::{
config::Config,
error::{Error, ErrorKind},
};
use activitystreams::iri_string::types::IriString;
2021-08-01 20:12:06 +00:00
use rsa::{
2022-04-08 22:39:38 +00:00
pkcs8::{DecodePrivateKey, EncodePrivateKey},
2021-08-01 20:12:06 +00:00
RsaPrivateKey,
};
use sled::{transaction::TransactionError, Batch, Transactional, Tree};
use std::{
collections::{BTreeMap, HashMap},
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
time::SystemTime,
};
use time::OffsetDateTime;
2021-02-10 04:05:06 +00:00
use uuid::Uuid;
2021-09-18 17:55:39 +00:00
#[derive(Clone, Debug)]
2021-02-10 04:17:20 +00:00
pub(crate) struct Db {
2021-02-10 04:05:06 +00:00
inner: Arc<Inner>,
2020-03-19 22:19:05 +00:00
}
2021-02-10 04:05:06 +00:00
struct Inner {
healthz: Tree,
healthz_counter: Arc<AtomicU64>,
2021-02-10 04:05:06 +00:00
actor_id_actor: Tree,
public_key_id_actor_id: Tree,
connected_actor_ids: Tree,
allowed_domains: Tree,
blocked_domains: Tree,
settings: Tree,
media_url_media_id: Tree,
media_id_media_url: Tree,
actor_id_info: Tree,
actor_id_instance: Tree,
actor_id_contact: Tree,
last_seen: Tree,
2021-02-10 04:05:06 +00:00
restricted_mode: bool,
}
2021-09-18 17:55:39 +00:00
impl std::fmt::Debug for Inner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Inner")
.field("restricted_mode", &self.restricted_mode)
.finish()
}
}
2021-09-21 19:32:25 +00:00
#[derive(Clone, serde::Deserialize, serde::Serialize)]
2021-02-10 04:05:06 +00:00
pub struct Actor {
2022-01-17 22:54:45 +00:00
pub(crate) id: IriString,
2021-02-10 04:05:06 +00:00
pub(crate) public_key: String,
2022-01-17 22:54:45 +00:00
pub(crate) public_key_id: IriString,
pub(crate) inbox: IriString,
2021-02-10 04:05:06 +00:00
pub(crate) saved_at: SystemTime,
}
2021-09-21 19:32:25 +00:00
impl std::fmt::Debug for Actor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Actor")
.field("id", &self.id.to_string())
.field("public_key", &self.public_key)
.field("public_key_id", &self.public_key_id.to_string())
.field("inbox", &self.inbox.to_string())
.field("saved_at", &self.saved_at)
.finish()
}
}
2021-02-10 04:05:06 +00:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Info {
pub(crate) software: String,
pub(crate) version: String,
pub(crate) reg: bool,
pub(crate) updated: SystemTime,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Instance {
pub(crate) title: String,
pub(crate) description: String,
pub(crate) version: String,
pub(crate) reg: bool,
pub(crate) requires_approval: bool,
pub(crate) updated: SystemTime,
}
2021-09-21 19:32:25 +00:00
#[derive(Clone, serde::Deserialize, serde::Serialize)]
2021-02-10 04:05:06 +00:00
pub struct Contact {
pub(crate) username: String,
pub(crate) display_name: String,
2022-01-17 22:54:45 +00:00
pub(crate) url: IriString,
pub(crate) avatar: IriString,
2021-02-10 04:05:06 +00:00
pub(crate) updated: SystemTime,
}
2021-09-21 19:32:25 +00:00
impl std::fmt::Debug for Contact {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Info")
.field("username", &self.username)
.field("display_name", &self.display_name)
.field("url", &self.url.to_string())
.field("avatar", &self.avatar.to_string())
.field("updated", &self.updated)
.finish()
}
}
2021-02-10 04:05:06 +00:00
impl Inner {
2022-01-17 22:54:45 +00:00
fn connected_by_domain(
&self,
domains: &[String],
) -> impl DoubleEndedIterator<Item = IriString> {
2021-11-23 22:19:59 +00:00
let reversed: Vec<_> = domains.iter().map(|s| domain_key(s.as_str())).collect();
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(url_from_ivec)
.filter_map(move |url| {
2022-01-17 22:54:45 +00:00
let connected_domain = url.authority_str()?;
2021-02-10 04:05:06 +00:00
let connected_rdnn = domain_key(connected_domain);
for rdnn in &reversed {
if connected_rdnn.starts_with(rdnn) {
return Some(url);
}
}
None
})
}
fn blocks(&self) -> impl DoubleEndedIterator<Item = String> {
self.blocked_domains
.iter()
.values()
.filter_map(|res| res.ok())
.map(|s| String::from_utf8_lossy(&s).to_string())
}
fn allowed(&self) -> impl DoubleEndedIterator<Item = String> {
self.allowed_domains
.iter()
.values()
.filter_map(|res| res.ok())
.map(|s| String::from_utf8_lossy(&s).to_string())
}
2022-01-17 22:54:45 +00:00
fn connected(&self) -> impl DoubleEndedIterator<Item = IriString> {
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(url_from_ivec)
}
2021-11-23 22:19:59 +00:00
fn connected_actors(&self) -> impl DoubleEndedIterator<Item = Actor> + '_ {
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(move |actor_id| {
let actor_ivec = self.actor_id_actor.get(actor_id).ok()??;
serde_json::from_slice::<Actor>(&actor_ivec).ok()
})
}
2022-01-17 22:54:45 +00:00
fn connected_info(&self) -> impl DoubleEndedIterator<Item = (IriString, Info)> + '_ {
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(move |actor_id_ivec| {
let actor_id = url_from_ivec(actor_id_ivec.clone())?;
let ivec = self.actor_id_info.get(actor_id_ivec).ok()??;
let info = serde_json::from_slice(&ivec).ok()?;
Some((actor_id, info))
})
}
2022-01-17 22:54:45 +00:00
fn connected_instance(&self) -> impl DoubleEndedIterator<Item = (IriString, Instance)> + '_ {
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(move |actor_id_ivec| {
let actor_id = url_from_ivec(actor_id_ivec.clone())?;
let ivec = self.actor_id_instance.get(actor_id_ivec).ok()??;
let instance = serde_json::from_slice(&ivec).ok()?;
Some((actor_id, instance))
})
}
2020-03-19 22:19:05 +00:00
2022-01-17 22:54:45 +00:00
fn connected_contact(&self) -> impl DoubleEndedIterator<Item = (IriString, Contact)> + '_ {
2021-02-10 04:05:06 +00:00
self.connected_actor_ids
.iter()
.values()
.filter_map(|res| res.ok())
.filter_map(move |actor_id_ivec| {
let actor_id = url_from_ivec(actor_id_ivec.clone())?;
let ivec = self.actor_id_contact.get(actor_id_ivec).ok()??;
let contact = serde_json::from_slice(&ivec).ok()?;
Some((actor_id, contact))
})
}
2022-01-17 22:54:45 +00:00
fn is_allowed(&self, authority: &str) -> bool {
let prefix = domain_prefix(authority);
let reverse_domain = domain_key(authority);
2021-02-10 04:05:06 +00:00
if self.restricted_mode {
self.allowed_domains
.scan_prefix(prefix)
.keys()
.filter_map(|res| res.ok())
2021-02-10 06:14:42 +00:00
.any(|rdnn| {
let rdnn_string = String::from_utf8_lossy(&rdnn);
reverse_domain.starts_with(rdnn_string.as_ref())
})
2021-02-10 04:05:06 +00:00
} else {
!self
.blocked_domains
.scan_prefix(prefix)
.keys()
.filter_map(|res| res.ok())
2021-02-10 06:14:42 +00:00
.any(|rdnn| reverse_domain.starts_with(String::from_utf8_lossy(&rdnn).as_ref()))
2021-02-10 04:05:06 +00:00
}
}
}
impl Db {
2021-09-18 17:55:39 +00:00
pub(crate) fn build(config: &Config) -> Result<Self, Error> {
2021-02-10 04:05:06 +00:00
let db = sled::open(config.sled_path())?;
2021-02-10 06:57:49 +00:00
Self::build_inner(config.restricted_mode(), db)
}
2020-03-19 22:19:05 +00:00
2021-09-18 17:55:39 +00:00
fn build_inner(restricted_mode: bool, db: sled::Db) -> Result<Self, Error> {
2020-04-21 17:07:39 +00:00
Ok(Db {
2021-02-10 04:05:06 +00:00
inner: Arc::new(Inner {
healthz: db.open_tree("healthz")?,
healthz_counter: Arc::new(AtomicU64::new(0)),
2021-02-10 04:05:06 +00:00
actor_id_actor: db.open_tree("actor-id-actor")?,
public_key_id_actor_id: db.open_tree("public-key-id-actor-id")?,
connected_actor_ids: db.open_tree("connected-actor-ids")?,
allowed_domains: db.open_tree("allowed-actor-ids")?,
blocked_domains: db.open_tree("blocked-actor-ids")?,
settings: db.open_tree("settings")?,
media_url_media_id: db.open_tree("media-url-media-id")?,
media_id_media_url: db.open_tree("media-id-media-url")?,
actor_id_info: db.open_tree("actor-id-info")?,
actor_id_instance: db.open_tree("actor-id-instance")?,
actor_id_contact: db.open_tree("actor-id-contact")?,
last_seen: db.open_tree("last-seen")?,
2021-02-10 04:05:06 +00:00
restricted_mode,
}),
2020-04-21 17:07:39 +00:00
})
2020-03-19 22:19:05 +00:00
}
2021-02-10 04:05:06 +00:00
async fn unblock<T>(
&self,
f: impl FnOnce(&Inner) -> Result<T, Error> + Send + 'static,
2021-09-18 17:55:39 +00:00
) -> Result<T, Error>
2021-02-10 04:05:06 +00:00
where
T: Send + 'static,
{
let inner = self.inner.clone();
2021-02-11 00:00:11 +00:00
let t = actix_web::web::block(move || (f)(&inner)).await??;
2021-02-10 04:05:06 +00:00
Ok(t)
}
pub(crate) async fn check_health(&self) -> Result<(), Error> {
let next = self.inner.healthz_counter.fetch_add(1, Ordering::Relaxed);
self.unblock(move |inner| {
let res = inner
.healthz
.insert("healthz", &next.to_be_bytes()[..])
.map_err(Error::from);
metrics::gauge!("relay.db.healthz.size")
.set(crate::collector::recordable(inner.healthz.len()));
res
})
.await?;
self.inner.healthz.flush_async().await?;
self.unblock(move |inner| inner.healthz.get("healthz").map_err(Error::from))
.await?;
Ok(())
}
pub(crate) async fn mark_last_seen(
&self,
nodes: HashMap<String, OffsetDateTime>,
) -> Result<(), Error> {
let mut batch = Batch::default();
for (domain, datetime) in nodes {
let datetime_string = serde_json::to_vec(&datetime)?;
batch.insert(domain.as_bytes(), datetime_string);
}
self.unblock(move |inner| inner.last_seen.apply_batch(batch).map_err(Error::from))
.await
}
pub(crate) async fn last_seen(
&self,
) -> Result<BTreeMap<String, Option<OffsetDateTime>>, Error> {
self.unblock(|inner| {
let mut map = BTreeMap::new();
for iri in inner.connected() {
let Some(authority_str) = iri.authority_str() else {
continue;
};
if let Some(datetime) = inner.last_seen.get(authority_str)? {
map.insert(
authority_str.to_string(),
Some(serde_json::from_slice(&datetime)?),
);
} else {
map.insert(authority_str.to_string(), None);
}
}
Ok(map)
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn connected_ids(&self) -> Result<Vec<IriString>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.connected().collect())).await
2020-03-22 21:18:36 +00:00
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn save_info(&self, actor_id: IriString, info: Info) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let vec = serde_json::to_vec(&info)?;
2020-03-19 22:19:05 +00:00
2021-02-10 04:05:06 +00:00
inner
.actor_id_info
.insert(actor_id.as_str().as_bytes(), vec)?;
metrics::gauge!("relay.db.actor-id-info.size")
.set(crate::collector::recordable(inner.actor_id_info.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
2020-03-19 22:19:05 +00:00
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn info(&self, actor_id: IriString) -> Result<Option<Info>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
inner
.actor_id_info
.get(actor_id.as_str().as_bytes())?
.map(|ivec| serde_json::from_slice(&ivec))
.transpose()
.map_err(Error::from)
2021-02-10 04:05:06 +00:00
})
.await
}
2020-03-19 22:19:05 +00:00
2022-01-17 22:54:45 +00:00
pub(crate) async fn connected_info(&self) -> Result<HashMap<IriString, Info>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.connected_info().collect()))
.await
2020-03-19 22:19:05 +00:00
}
2021-02-10 04:05:06 +00:00
pub(crate) async fn save_instance(
&self,
2022-01-17 22:54:45 +00:00
actor_id: IriString,
2021-02-10 04:05:06 +00:00
instance: Instance,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let vec = serde_json::to_vec(&instance)?;
inner
.actor_id_instance
.insert(actor_id.as_str().as_bytes(), vec)?;
metrics::gauge!("relay.db.actor-id-instance.size")
.set(crate::collector::recordable(inner.actor_id_instance.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn instance(&self, actor_id: IriString) -> Result<Option<Instance>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
inner
.actor_id_instance
.get(actor_id.as_str().as_bytes())?
.map(|ivec| serde_json::from_slice(&ivec))
.transpose()
.map_err(Error::from)
2021-02-10 04:05:06 +00:00
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn connected_instance(&self) -> Result<HashMap<IriString, Instance>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.connected_instance().collect()))
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn save_contact(
&self,
actor_id: IriString,
contact: Contact,
) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let vec = serde_json::to_vec(&contact)?;
inner
.actor_id_contact
.insert(actor_id.as_str().as_bytes(), vec)?;
metrics::gauge!("relay.db.actor-id-contact.size")
.set(crate::collector::recordable(inner.actor_id_contact.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn contact(&self, actor_id: IriString) -> Result<Option<Contact>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
inner
.actor_id_contact
.get(actor_id.as_str().as_bytes())?
.map(|ivec| serde_json::from_slice(&ivec))
.transpose()
.map_err(Error::from)
2021-02-10 04:05:06 +00:00
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn connected_contact(&self) -> Result<HashMap<IriString, Contact>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.connected_contact().collect()))
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn save_url(&self, url: IriString, id: Uuid) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
inner
.media_id_media_url
.insert(id.as_bytes(), url.as_str().as_bytes())?;
inner
.media_url_media_id
.insert(url.as_str().as_bytes(), id.as_bytes())?;
metrics::gauge!("relay.db.media-id-media-url.size")
.set(crate::collector::recordable(inner.media_id_media_url.len()));
metrics::gauge!("relay.db.media-url-media-id.size")
.set(crate::collector::recordable(inner.media_url_media_id.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn media_id(&self, url: IriString) -> Result<Option<Uuid>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
Ok(inner
.media_url_media_id
.get(url.as_str().as_bytes())?
.and_then(uuid_from_ivec))
2021-02-10 04:05:06 +00:00
})
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<IriString>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
Ok(inner
.media_id_media_url
.get(id.as_bytes())?
.and_then(url_from_ivec))
2021-02-10 04:05:06 +00:00
})
.await
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn blocks(&self) -> Result<Vec<String>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.blocks().collect())).await
}
2020-03-19 22:19:05 +00:00
pub(crate) async fn allows(&self) -> Result<Vec<String>, Error> {
self.unblock(|inner| Ok(inner.allowed().collect())).await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn inboxes(&self) -> Result<Vec<IriString>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| Ok(inner.connected_actors().map(|actor| actor.inbox).collect()))
.await
2020-03-19 22:19:05 +00:00
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn is_connected(&self, base_id: IriString) -> Result<bool, Error> {
let scheme = base_id.scheme_str();
let authority = base_id.authority_str().ok_or(ErrorKind::MissingDomain)?;
let prefix = format!("{scheme}://{authority}");
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let connected = inner
.connected_actor_ids
2022-01-17 22:54:45 +00:00
.scan_prefix(prefix.as_bytes())
.values()
2022-01-17 22:54:45 +00:00
.any(|res| res.is_ok());
2020-03-19 22:19:05 +00:00
2021-02-10 04:05:06 +00:00
Ok(connected)
})
.await
}
pub(crate) async fn actor_id_from_public_key_id(
&self,
2022-01-17 22:54:45 +00:00
public_key_id: IriString,
) -> Result<Option<IriString>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
Ok(inner
2021-02-10 04:05:06 +00:00
.public_key_id_actor_id
.get(public_key_id.as_str().as_bytes())?
2023-02-06 03:09:47 +00:00
.and_then(url_from_ivec))
2021-02-10 04:05:06 +00:00
})
.await
2020-03-19 22:19:05 +00:00
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn actor(&self, actor_id: IriString) -> Result<Option<Actor>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2023-02-06 03:09:47 +00:00
inner
.actor_id_actor
.get(actor_id.as_str().as_bytes())?
.map(|ivec| serde_json::from_slice(&ivec))
.transpose()
.map_err(Error::from)
2021-02-10 04:05:06 +00:00
})
.await
}
2020-04-21 17:07:39 +00:00
2021-09-18 17:55:39 +00:00
pub(crate) async fn save_actor(&self, actor: Actor) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let vec = serde_json::to_vec(&actor)?;
inner.public_key_id_actor_id.insert(
actor.public_key_id.as_str().as_bytes(),
actor.id.as_str().as_bytes(),
)?;
inner
.actor_id_actor
.insert(actor.id.as_str().as_bytes(), vec)?;
metrics::gauge!("relay.db.public-key-actor-id.size").set(crate::collector::recordable(
inner.public_key_id_actor_id.len(),
));
metrics::gauge!("relay.db.actor-id-actor.size").set(crate::collector::recordable(
inner.public_key_id_actor_id.len(),
));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2020-04-21 19:31:32 +00:00
2022-01-17 22:54:45 +00:00
pub(crate) async fn remove_connection(&self, actor_id: IriString) -> Result<(), Error> {
tracing::debug!("Removing Connection: {actor_id}");
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
inner
.connected_actor_ids
.remove(actor_id.as_str().as_bytes())?;
2020-03-19 22:19:05 +00:00
metrics::gauge!("relay.db.connected-actor-ids.size").set(crate::collector::recordable(
inner.connected_actor_ids.len(),
));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
2020-03-19 22:19:05 +00:00
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn add_connection(&self, actor_id: IriString) -> Result<(), Error> {
tracing::debug!("Adding Connection: {actor_id}");
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
inner
.connected_actor_ids
.insert(actor_id.as_str().as_bytes(), actor_id.as_str().as_bytes())?;
2020-03-19 22:19:05 +00:00
metrics::gauge!("relay.db.connected-actor-ids.size").set(crate::collector::recordable(
inner.connected_actor_ids.len(),
));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
2020-03-19 22:19:05 +00:00
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn add_blocks(&self, domains: Vec<String>) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let connected_by_domain = inner.connected_by_domain(&domains).collect::<Vec<_>>();
let res = (
&inner.connected_actor_ids,
&inner.blocked_domains,
&inner.allowed_domains,
)
.transaction(|(connected, blocked, allowed)| {
2024-04-07 16:40:57 +00:00
let mut connected_batch = Batch::default();
let mut blocked_batch = Batch::default();
let mut allowed_batch = Batch::default();
for connected in &connected_by_domain {
2024-04-07 16:40:57 +00:00
connected_batch.remove(connected.as_str().as_bytes());
}
for authority in &domains {
blocked_batch
.insert(domain_key(authority).as_bytes(), authority.as_bytes());
allowed_batch.remove(domain_key(authority).as_bytes());
}
connected.apply_batch(&connected_batch)?;
blocked.apply_batch(&blocked_batch)?;
allowed.apply_batch(&allowed_batch)?;
Ok(())
});
metrics::gauge!("relay.db.connected-actor-ids.size").set(crate::collector::recordable(
inner.connected_actor_ids.len(),
));
metrics::gauge!("relay.db.blocked-domains.size")
.set(crate::collector::recordable(inner.blocked_domains.len()));
metrics::gauge!("relay.db.allowed-domains.size")
.set(crate::collector::recordable(inner.allowed_domains.len()));
match res {
Ok(()) => Ok(()),
Err(TransactionError::Abort(e) | TransactionError::Storage(e)) => Err(e.into()),
}
2021-02-10 04:05:06 +00:00
})
.await
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn remove_blocks(&self, domains: Vec<String>) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let mut blocked_batch = Batch::default();
2022-01-17 22:54:45 +00:00
for authority in &domains {
2024-04-07 16:40:57 +00:00
blocked_batch.remove(domain_key(authority).as_bytes());
2021-02-10 04:05:06 +00:00
}
inner.blocked_domains.apply_batch(blocked_batch)?;
metrics::gauge!("relay.db.blocked-domains.size")
.set(crate::collector::recordable(inner.blocked_domains.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn add_allows(&self, domains: Vec<String>) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
let mut allowed_batch = Batch::default();
2022-01-17 22:54:45 +00:00
for authority in &domains {
2024-04-07 16:40:57 +00:00
allowed_batch.insert(domain_key(authority).as_bytes(), authority.as_bytes());
2021-02-10 04:05:06 +00:00
}
inner.allowed_domains.apply_batch(allowed_batch)?;
metrics::gauge!("relay.db.allowed-domains.size")
.set(crate::collector::recordable(inner.allowed_domains.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn remove_allows(&self, domains: Vec<String>) -> Result<(), Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
if inner.restricted_mode {
let connected_by_domain = inner.connected_by_domain(&domains).collect::<Vec<_>>();
let mut connected_batch = Batch::default();
for connected in &connected_by_domain {
connected_batch.remove(connected.as_str().as_bytes());
2020-03-18 22:37:22 +00:00
}
inner.connected_actor_ids.apply_batch(connected_batch)?;
metrics::gauge!("relay.db.connected-actor-ids.size").set(
crate::collector::recordable(inner.connected_actor_ids.len()),
);
2020-03-18 22:37:22 +00:00
}
2021-02-10 04:05:06 +00:00
let mut allowed_batch = Batch::default();
2022-01-17 22:54:45 +00:00
for authority in &domains {
2024-04-07 16:40:57 +00:00
allowed_batch.remove(domain_key(authority).as_bytes());
2021-02-10 04:05:06 +00:00
}
inner.allowed_domains.apply_batch(allowed_batch)?;
metrics::gauge!("relay.db.allowed-domains.size")
.set(crate::collector::recordable(inner.allowed_domains.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
2021-02-10 04:05:06 +00:00
.await
}
2022-01-17 22:54:45 +00:00
pub(crate) async fn is_allowed(&self, url: IriString) -> Result<bool, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
2022-01-17 22:54:45 +00:00
if let Some(authority) = url.authority_str() {
Ok(inner.is_allowed(authority))
2021-02-10 04:05:06 +00:00
} else {
Ok(false)
}
})
.await
}
2021-09-18 17:55:39 +00:00
pub(crate) async fn private_key(&self) -> Result<Option<RsaPrivateKey>, Error> {
2021-02-10 04:05:06 +00:00
self.unblock(|inner| {
if let Some(ivec) = inner.settings.get("private-key")? {
let key_str = String::from_utf8_lossy(&ivec);
2021-08-01 20:12:06 +00:00
let key = RsaPrivateKey::from_pkcs8_pem(&key_str)?;
2021-02-10 04:05:06 +00:00
Ok(Some(key))
} else {
Ok(None)
}
})
.await
}
pub(crate) async fn update_private_key(
&self,
2021-08-01 20:12:06 +00:00
private_key: &RsaPrivateKey,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2022-04-08 22:39:38 +00:00
let pem_pkcs8 = private_key.to_pkcs8_pem(rsa::pkcs8::LineEnding::default())?;
2021-02-10 04:05:06 +00:00
self.unblock(move |inner| {
inner
.settings
.insert("private-key".as_bytes(), pem_pkcs8.as_bytes())?;
metrics::gauge!("relay.db.settings.size")
.set(crate::collector::recordable(inner.settings.len()));
2021-02-10 04:05:06 +00:00
Ok(())
})
.await
}
}
2022-01-17 22:54:45 +00:00
fn domain_key(authority: &str) -> String {
authority.split('.').rev().collect::<Vec<_>>().join(".") + "."
2021-02-10 04:05:06 +00:00
}
2022-01-17 22:54:45 +00:00
fn domain_prefix(authority: &str) -> String {
authority
2021-02-10 04:05:06 +00:00
.split('.')
.rev()
.take(2)
.collect::<Vec<_>>()
.join(".")
+ "."
}
2022-01-17 22:54:45 +00:00
fn url_from_ivec(ivec: sled::IVec) -> Option<IriString> {
String::from_utf8_lossy(&ivec).parse::<IriString>().ok()
2021-02-10 04:05:06 +00:00
}
2021-02-10 04:05:06 +00:00
fn uuid_from_ivec(ivec: sled::IVec) -> Option<Uuid> {
Uuid::from_slice(&ivec).ok()
}
2021-02-10 06:57:49 +00:00
#[cfg(test)]
mod tests {
use super::Db;
2022-01-17 22:54:45 +00:00
use activitystreams::iri_string::types::IriString;
2021-02-10 06:57:49 +00:00
use std::future::Future;
#[test]
fn connect_and_verify() {
run(|db| async move {
2022-01-17 22:54:45 +00:00
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
let example_sub_actor: IriString = "http://example.com/users/fake".parse().unwrap();
2021-02-10 06:57:49 +00:00
db.add_connection(example_actor.clone()).await.unwrap();
assert!(db.is_connected(example_sub_actor).await.unwrap());
2021-02-10 06:57:49 +00:00
})
}
2021-02-10 15:47:23 +00:00
#[test]
fn disconnect_and_verify() {
run(|db| async move {
2022-01-17 22:54:45 +00:00
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
let example_sub_actor: IriString = "http://example.com/users/fake".parse().unwrap();
2021-02-10 15:47:23 +00:00
db.add_connection(example_actor.clone()).await.unwrap();
assert!(db.is_connected(example_sub_actor.clone()).await.unwrap());
db.remove_connection(example_actor).await.unwrap();
assert!(!db.is_connected(example_sub_actor).await.unwrap());
})
}
#[test]
fn connected_actor_in_connected_list() {
run(|db| async move {
2022-01-17 22:54:45 +00:00
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
2021-02-10 15:47:23 +00:00
db.add_connection(example_actor.clone()).await.unwrap();
assert!(db.connected_ids().await.unwrap().contains(&example_actor));
})
}
#[test]
fn disconnected_actor_not_in_connected_list() {
run(|db| async move {
2022-01-17 22:54:45 +00:00
let example_actor: IriString = "http://example.com/actor".parse().unwrap();
2021-02-10 15:47:23 +00:00
db.add_connection(example_actor.clone()).await.unwrap();
db.remove_connection(example_actor.clone()).await.unwrap();
assert!(!db.connected_ids().await.unwrap().contains(&example_actor));
})
}
2021-02-10 06:57:49 +00:00
fn run<F, Fut>(f: F)
where
F: Fn(Db) -> Fut,
Fut: Future<Output = ()> + 'static,
{
let db =
Db::build_inner(true, sled::Config::new().temporary(true).open().unwrap()).unwrap();
2024-01-14 20:56:07 +00:00
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on((f)(db));
2021-02-10 06:57:49 +00:00
}
}