2021-09-18 17:55:39 +00:00
|
|
|
use crate::{config::Config, error::Error};
|
2020-09-07 21:51:02 +00:00
|
|
|
use activitystreams::url::Url;
|
2021-02-10 04:05:06 +00:00
|
|
|
use actix_web::web::Bytes;
|
2021-08-01 20:12:06 +00:00
|
|
|
use rsa::{
|
|
|
|
pkcs8::{FromPrivateKey, ToPrivateKey},
|
|
|
|
RsaPrivateKey,
|
|
|
|
};
|
2021-02-10 04:05:06 +00:00
|
|
|
use sled::Tree;
|
|
|
|
use std::{collections::HashMap, sync::Arc, time::SystemTime};
|
|
|
|
use uuid::Uuid;
|
2020-03-16 17:56:26 +00:00
|
|
|
|
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 {
|
|
|
|
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,
|
|
|
|
media_id_media_bytes: Tree,
|
|
|
|
media_id_media_meta: Tree,
|
|
|
|
actor_id_info: Tree,
|
|
|
|
actor_id_instance: Tree,
|
|
|
|
actor_id_contact: Tree,
|
|
|
|
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-02-10 04:05:06 +00:00
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
|
|
pub struct Actor {
|
|
|
|
pub(crate) id: Url,
|
|
|
|
pub(crate) public_key: String,
|
|
|
|
pub(crate) public_key_id: Url,
|
|
|
|
pub(crate) inbox: Url,
|
|
|
|
pub(crate) saved_at: SystemTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
2021-02-10 04:17:20 +00:00
|
|
|
pub(crate) struct MediaMeta {
|
2021-02-10 04:05:06 +00:00
|
|
|
pub(crate) media_type: String,
|
|
|
|
pub(crate) saved_at: SystemTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
|
|
pub struct Contact {
|
|
|
|
pub(crate) username: String,
|
|
|
|
pub(crate) display_name: String,
|
|
|
|
pub(crate) url: Url,
|
|
|
|
pub(crate) avatar: Url,
|
|
|
|
pub(crate) updated: SystemTime,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Inner {
|
|
|
|
fn connected_by_domain(&self, domains: &[String]) -> impl DoubleEndedIterator<Item = Url> {
|
|
|
|
let reversed: Vec<_> = domains
|
|
|
|
.into_iter()
|
|
|
|
.map(|s| domain_key(s.as_str()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
self.connected_actor_ids
|
|
|
|
.iter()
|
|
|
|
.values()
|
|
|
|
.filter_map(|res| res.ok())
|
|
|
|
.filter_map(url_from_ivec)
|
|
|
|
.filter_map(move |url| {
|
|
|
|
let connected_domain = url.domain()?;
|
|
|
|
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 connected(&self) -> impl DoubleEndedIterator<Item = Url> {
|
|
|
|
self.connected_actor_ids
|
|
|
|
.iter()
|
|
|
|
.values()
|
|
|
|
.filter_map(|res| res.ok())
|
|
|
|
.filter_map(url_from_ivec)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connected_actors<'a>(&'a self) -> impl DoubleEndedIterator<Item = Actor> + 'a {
|
|
|
|
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()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connected_info<'a>(&'a self) -> impl DoubleEndedIterator<Item = (Url, Info)> + 'a {
|
|
|
|
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))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connected_instance<'a>(&'a self) -> impl DoubleEndedIterator<Item = (Url, Instance)> + 'a {
|
|
|
|
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
|
|
|
|
2021-02-10 04:05:06 +00:00
|
|
|
fn connected_contact<'a>(&'a self) -> impl DoubleEndedIterator<Item = (Url, Contact)> + 'a {
|
|
|
|
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))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_allowed(&self, domain: &str) -> bool {
|
|
|
|
let prefix = domain_prefix(domain);
|
2021-02-10 06:14:42 +00:00
|
|
|
let reverse_domain = domain_key(domain);
|
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 {
|
|
|
|
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")?,
|
|
|
|
media_id_media_bytes: db.open_tree("media-id-media-bytes")?,
|
|
|
|
media_id_media_meta: db.open_tree("media-id-media-meta")?,
|
|
|
|
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")?,
|
|
|
|
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,
|
2021-09-18 17:55:39 +00:00
|
|
|
f: impl Fn(&Inner) -> Result<T, Error> + Send + 'static,
|
|
|
|
) -> 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)
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn connected_ids(&self) -> Result<Vec<Url>, 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
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn save_info(&self, actor_id: Url, 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)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
2020-03-19 22:19:05 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn info(&self, actor_id: Url) -> Result<Option<Info>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.actor_id_info.get(actor_id.as_str().as_bytes())? {
|
|
|
|
let info = serde_json::from_slice(&ivec)?;
|
|
|
|
Ok(Some(info))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2020-03-19 22:19:05 +00:00
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn connected_info(&self) -> Result<HashMap<Url, 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,
|
|
|
|
actor_id: Url,
|
|
|
|
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)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
2020-03-20 00:55:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn instance(&self, actor_id: Url) -> Result<Option<Instance>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.actor_id_instance.get(actor_id.as_str().as_bytes())? {
|
|
|
|
let instance = serde_json::from_slice(&ivec)?;
|
|
|
|
Ok(Some(instance))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
2020-03-20 00:55:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn connected_instance(&self) -> Result<HashMap<Url, Instance>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(|inner| Ok(inner.connected_instance().collect()))
|
|
|
|
.await
|
2020-03-20 00:55:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn save_contact(&self, actor_id: Url, 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)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn contact(&self, actor_id: Url) -> Result<Option<Contact>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.actor_id_contact.get(actor_id.as_str().as_bytes())? {
|
|
|
|
let contact = serde_json::from_slice(&ivec)?;
|
|
|
|
Ok(Some(contact))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn connected_contact(&self) -> Result<HashMap<Url, Contact>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(|inner| Ok(inner.connected_contact().collect()))
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn save_url(&self, url: Url, 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())?;
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn save_bytes(
|
|
|
|
&self,
|
|
|
|
id: Uuid,
|
|
|
|
meta: MediaMeta,
|
|
|
|
bytes: Bytes,
|
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(&meta)?;
|
|
|
|
|
|
|
|
inner
|
|
|
|
.media_id_media_bytes
|
|
|
|
.insert(id.as_bytes(), bytes.as_ref())?;
|
|
|
|
inner.media_id_media_meta.insert(id.as_bytes(), vec)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn media_id(&self, url: Url) -> Result<Option<Uuid>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? {
|
|
|
|
Ok(uuid_from_ivec(ivec))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
2020-03-20 00:55:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn media_url(&self, id: Uuid) -> Result<Option<Url>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.media_id_media_url.get(id.as_bytes())? {
|
|
|
|
Ok(url_from_ivec(ivec))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn media_bytes(&self, id: Uuid) -> Result<Option<Bytes>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.media_id_media_bytes.get(id.as_bytes())? {
|
|
|
|
Ok(Some(Bytes::copy_from_slice(&ivec)))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2020-03-19 22:19:05 +00:00
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn media_meta(&self, id: Uuid) -> Result<Option<MediaMeta>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.media_id_media_meta.get(id.as_bytes())? {
|
|
|
|
let meta = serde_json::from_slice(&ivec)?;
|
|
|
|
Ok(Some(meta))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
2020-03-19 22:19:05 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn inboxes(&self) -> Result<Vec<Url>, 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
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn is_connected(&self, mut id: Url) -> Result<bool, Error> {
|
2021-02-10 15:23:55 +00:00
|
|
|
id.set_path("");
|
|
|
|
id.set_query(None);
|
|
|
|
id.set_fragment(None);
|
|
|
|
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
let connected = inner
|
|
|
|
.connected_actor_ids
|
2021-02-10 15:23:55 +00:00
|
|
|
.scan_prefix(id.as_str().as_bytes())
|
|
|
|
.values()
|
|
|
|
.filter_map(|res| res.ok())
|
|
|
|
.next()
|
|
|
|
.is_some();
|
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,
|
|
|
|
public_key_id: Url,
|
2021-09-18 17:55:39 +00:00
|
|
|
) -> Result<Option<Url>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner
|
|
|
|
.public_key_id_actor_id
|
|
|
|
.get(public_key_id.as_str().as_bytes())?
|
|
|
|
{
|
|
|
|
Ok(url_from_ivec(ivec))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
2020-03-19 22:19:05 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn actor(&self, actor_id: Url) -> Result<Option<Actor>, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(ivec) = inner.actor_id_actor.get(actor_id.as_str().as_bytes())? {
|
|
|
|
let actor = serde_json::from_slice(&ivec)?;
|
|
|
|
Ok(Some(actor))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.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)?;
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2020-04-21 19:31:32 +00:00
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn remove_connection(&self, actor_id: Url) -> 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
|
|
|
|
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_connection(&self, actor_id: Url) -> 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
|
|
|
|
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| {
|
|
|
|
for connected in inner.connected_by_domain(&domains) {
|
|
|
|
inner
|
|
|
|
.connected_actor_ids
|
|
|
|
.remove(connected.as_str().as_bytes())?;
|
|
|
|
}
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2021-02-10 04:05:06 +00:00
|
|
|
for domain in &domains {
|
|
|
|
inner
|
|
|
|
.blocked_domains
|
|
|
|
.insert(domain_key(domain), domain.as_bytes())?;
|
|
|
|
inner.allowed_domains.remove(domain_key(domain))?;
|
|
|
|
}
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2021-02-10 04:05:06 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2020-03-20 00:55:11 +00:00
|
|
|
|
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| {
|
|
|
|
for domain in &domains {
|
|
|
|
inner.blocked_domains.remove(domain_key(domain))?;
|
|
|
|
}
|
2020-03-20 00:55:11 +00:00
|
|
|
|
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| {
|
|
|
|
for domain in &domains {
|
|
|
|
inner
|
|
|
|
.allowed_domains
|
|
|
|
.insert(domain_key(domain), domain.as_bytes())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2020-03-16 17:56:26 +00:00
|
|
|
|
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 {
|
|
|
|
for connected in inner.connected_by_domain(&domains) {
|
|
|
|
inner
|
|
|
|
.connected_actor_ids
|
|
|
|
.remove(connected.as_str().as_bytes())?;
|
2020-03-18 22:37:22 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-10 04:05:06 +00:00
|
|
|
|
|
|
|
for domain in &domains {
|
|
|
|
inner.allowed_domains.remove(domain_key(domain))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2020-03-16 17:56:26 +00:00
|
|
|
})
|
2021-02-10 04:05:06 +00:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-09-18 17:55:39 +00:00
|
|
|
pub(crate) async fn is_allowed(&self, url: Url) -> Result<bool, Error> {
|
2021-02-10 04:05:06 +00:00
|
|
|
self.unblock(move |inner| {
|
|
|
|
if let Some(domain) = url.domain() {
|
|
|
|
Ok(inner.is_allowed(domain))
|
|
|
|
} 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> {
|
2021-08-01 20:12:06 +00:00
|
|
|
let pem_pkcs8 = private_key.to_pkcs8_pem()?;
|
2021-02-10 04:05:06 +00:00
|
|
|
|
|
|
|
self.unblock(move |inner| {
|
|
|
|
inner
|
|
|
|
.settings
|
|
|
|
.insert("private-key".as_bytes(), pem_pkcs8.as_bytes())?;
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn domain_key(domain: &str) -> String {
|
|
|
|
domain.split('.').rev().collect::<Vec<_>>().join(".") + "."
|
|
|
|
}
|
|
|
|
|
|
|
|
fn domain_prefix(domain: &str) -> String {
|
|
|
|
domain
|
|
|
|
.split('.')
|
|
|
|
.rev()
|
|
|
|
.take(2)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(".")
|
|
|
|
+ "."
|
|
|
|
}
|
|
|
|
|
|
|
|
fn url_from_ivec(ivec: sled::IVec) -> Option<Url> {
|
|
|
|
String::from_utf8_lossy(&ivec).parse::<Url>().ok()
|
|
|
|
}
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2021-02-10 04:05:06 +00:00
|
|
|
fn uuid_from_ivec(ivec: sled::IVec) -> Option<Uuid> {
|
|
|
|
Uuid::from_slice(&ivec).ok()
|
2020-03-16 17:56:26 +00:00
|
|
|
}
|
2021-02-10 06:57:49 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::Db;
|
|
|
|
use activitystreams::url::Url;
|
|
|
|
use std::future::Future;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn connect_and_verify() {
|
|
|
|
run(|db| async move {
|
|
|
|
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
2021-02-10 15:23:55 +00:00
|
|
|
let example_sub_actor: Url = "http://example.com/users/fake".parse().unwrap();
|
2021-02-10 06:57:49 +00:00
|
|
|
db.add_connection(example_actor.clone()).await.unwrap();
|
2021-02-10 15:23:55 +00:00
|
|
|
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 {
|
|
|
|
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
|
|
|
let example_sub_actor: Url = "http://example.com/users/fake".parse().unwrap();
|
|
|
|
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 {
|
|
|
|
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
|
|
|
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 {
|
|
|
|
let example_actor: Url = "http://example.com/actor".parse().unwrap();
|
|
|
|
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();
|
2021-02-11 19:41:03 +00:00
|
|
|
actix_rt::System::new().block_on((f)(db));
|
2021-02-10 06:57:49 +00:00
|
|
|
}
|
|
|
|
}
|