1mod collection;
14mod inbox;
15mod negotiate;
16mod nodeinfo;
17mod signer;
18mod webfinger;
19
20pub use crate::template::Values;
21pub use collection::{Collection, CollectionDocument, First, Page};
22pub use inbox::{
23 CollectionRef, Forward, ForwardTo, GatewayInbox, InboxWorker, InboxWorkerConfig,
24 MAX_BODY as MAX_INBOX_BODY, Received, VouchedBy,
25};
26pub use nodeinfo::{NodeInfo, Software, Usage};
27pub use signer::{KnownKey, Signing};
28
29use crate::fetch::Fetcher;
30use crate::kv::{KvError, KvStore};
31use crate::portable::{ApUri, Hashlink, Media};
32use crate::template::{Template, TemplateError};
33use chrono::{DateTime, SecondsFormat, Utc};
34use http::{HeaderName, HeaderValue, Method, StatusCode, header};
35use serde_json::{Value, json};
36use std::fmt;
37use std::future::Future;
38use std::pin::Pin;
39use std::sync::Arc;
40use std::time::Duration;
41use url::Url;
42
43pub type Error = Box<dyn std::error::Error + Send + Sync>;
46
47type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
48
49pub const ACTIVITY_JSON: &str = "application/activity+json";
51
52#[must_use]
56pub fn default_context() -> Value {
57 json!([
58 "https://www.w3.org/ns/activitystreams",
59 "https://w3id.org/security/v1",
60 "https://w3id.org/security/multikey/v1",
61 ])
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub enum Access {
67 Allow,
69 Unauthorized,
72 Forbidden,
74 NotFound,
76}
77
78#[derive(Clone, Debug, PartialEq)]
80pub enum Found<T> {
81 Found(T),
82 Gone(Option<DateTime<Utc>>),
85 NotFound,
86 Redirect(Url),
89}
90
91#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
93pub struct ActorRef {
94 pub kind: String,
95 pub identifier: String,
96}
97
98impl ActorRef {
99 #[must_use]
100 pub fn new(kind: impl Into<String>, identifier: impl Into<String>) -> Self {
101 Self {
102 kind: kind.into(),
103 identifier: identifier.into(),
104 }
105 }
106}
107
108#[derive(Clone, Debug, Eq, PartialEq)]
110pub enum Route {
111 Actor(ActorRef),
112 Object { kind: String, values: Values },
113 Collection { kind: String, identifier: String },
114}
115
116#[derive(Clone, Debug, Eq, PartialEq)]
118pub enum PublicKey {
119 Rsa { id: String, pem: String },
122 Multikey { id: String, multibase: String },
125}
126
127pub fn with_keys(actor: &mut Value, keys: &[PublicKey]) {
130 let Some(members) = actor.as_object_mut() else {
131 return;
132 };
133 let id = members.get("id").cloned().unwrap_or(Value::Null);
134 if let Some((key_id, pem)) = keys.iter().find_map(|key| match key {
135 PublicKey::Rsa { id, pem } => Some((id, pem)),
136 PublicKey::Multikey { .. } => None,
137 }) {
138 members.insert(
139 "publicKey".into(),
140 json!({"id": key_id, "owner": id, "publicKeyPem": pem}),
141 );
142 }
143 let methods: Vec<Value> = keys
144 .iter()
145 .filter_map(|key| match key {
146 PublicKey::Multikey {
147 id: key_id,
148 multibase,
149 } => Some(json!({
150 "id": key_id,
151 "type": "Multikey",
152 "controller": id,
153 "publicKeyMultibase": multibase,
154 })),
155 PublicKey::Rsa { .. } => None,
156 })
157 .collect();
158 if !methods.is_empty() {
159 members.insert("assertionMethod".into(), Value::Array(methods));
160 }
161}
162
163#[derive(Debug)]
165pub enum Handled {
166 Response(http::Response<Vec<u8>>),
168 NotAcceptable,
171 NotFound,
173}
174
175#[derive(Clone, Debug, Eq, PartialEq)]
177pub enum UriError {
178 UnknownKind(String),
180 Template(TemplateError),
182}
183
184impl fmt::Display for UriError {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 match self {
187 Self::UnknownKind(kind) => write!(f, "no dispatcher of kind {kind:?}"),
188 Self::Template(error) => error.fmt(f),
189 }
190 }
191}
192
193impl std::error::Error for UriError {}
194
195#[derive(Clone, Debug, Eq, PartialEq)]
197pub struct BuildError(pub Vec<String>);
198
199impl fmt::Display for BuildError {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 f.write_str(&self.0.join("; "))
202 }
203}
204
205impl std::error::Error for BuildError {}
206
207type ActorFn<D> = Arc<
208 dyn Fn(Context<D>, String) -> BoxFuture<'static, Result<Found<Value>, Error>> + Send + Sync,
209>;
210type ObjectFn<D> = Arc<
211 dyn Fn(Context<D>, Values) -> BoxFuture<'static, Result<Found<Value>, Error>> + Send + Sync,
212>;
213type AuthorizeFn<D> =
214 Arc<dyn Fn(Context<D>, Values) -> BoxFuture<'static, Result<Access, Error>> + Send + Sync>;
215type KeysFn<D> = Arc<
216 dyn Fn(Context<D>, ActorRef) -> BoxFuture<'static, Result<Vec<PublicKey>, Error>> + Send + Sync,
217>;
218type LookupFn<D, A> =
219 Arc<dyn Fn(Context<D>, A) -> BoxFuture<'static, Result<Option<ActorRef>, Error>> + Send + Sync>;
220type LinksFn<D> = Arc<dyn Fn(&Context<D>, &ActorRef, &Value) -> Vec<Value> + Send + Sync>;
221type NodeInfoFn<D> =
222 Arc<dyn Fn(Context<D>) -> BoxFuture<'static, Result<NodeInfo, Error>> + Send + Sync>;
223type OriginFn<D> = Arc<dyn Fn(&str, &D) -> Option<Url> + Send + Sync>;
224type ErrorFn = Arc<dyn Fn(&Error) + Send + Sync>;
225type GatewayFn<D> =
226 Arc<dyn Fn(Context<D>, ApUri) -> BoxFuture<'static, Result<Found<Value>, Error>> + Send + Sync>;
227type GatewayMediaFn<D> = Arc<
228 dyn Fn(Context<D>, Hashlink) -> BoxFuture<'static, Result<Option<Media>, Error>> + Send + Sync,
229>;
230type GatewayInboxFn<D> = Arc<
231 dyn Fn(Context<D>, ApUri) -> BoxFuture<'static, Result<Option<GatewayInbox>, Error>>
232 + Send
233 + Sync,
234>;
235
236enum Dispatcher<D> {
237 Actor(ActorFn<D>),
238 Object(ObjectFn<D>),
239 Collection(Collection<D>),
240}
241
242struct Entry<D> {
243 kind: String,
244 template: Template,
245 dispatcher: Dispatcher<D>,
246 authorize: Option<AuthorizeFn<D>>,
247 alias: bool,
249}
250
251impl<D> Entry<D> {
252 fn noun(&self) -> &'static str {
253 match self.dispatcher {
254 Dispatcher::Actor(_) => "actor",
255 Dispatcher::Object(_) => "object",
256 Dispatcher::Collection(_) => "collection",
257 }
258 }
259}
260
261enum OriginRule<D> {
262 Fixed(Url),
263 PerHost(OriginFn<D>),
264}
265
266struct Inner<D> {
267 origin: OriginRule<D>,
268 entries: Vec<Entry<D>>,
269 templates: Arc<Templates>,
270 key_pairs: Option<KeysFn<D>>,
271 handle: Option<LookupFn<D, String>>,
272 map_alias: Option<LookupFn<D, Url>>,
273 webfinger_links: Option<LinksFn<D>>,
274 nodeinfo: Option<NodeInfoFn<D>>,
275 signed_fetch: Option<signer::SignedFetch<D>>,
276 on_error: Option<ErrorFn>,
277 inboxes: Vec<(String, Template)>,
278 shared_inbox: Option<Template>,
279 listeners: std::collections::HashMap<&'static str, inbox::ListenerFn<D>>,
280 fallback_listener: Option<inbox::ListenerFn<D>>,
281 blocked: Option<inbox::BlockedFn<D>>,
282 on_unverified: Option<inbox::UnverifiedFn<D>>,
283 inbox_queue: Option<inbox::QueueFn<D>>,
284 gateway: Option<GatewayFn<D>>,
285 gateway_media: Option<GatewayMediaFn<D>>,
286 gateway_inbox: Option<GatewayInboxFn<D>>,
287 forward: Option<inbox::ForwardFn<D>>,
288 read_as_written: bool,
289 linked_data_first: bool,
290 remote_contexts: Option<RemoteContexts<D>>,
291}
292
293pub(super) struct RemoteContexts<D> {
296 pub(super) limits: crate::contexts::Limits,
297 pub(super) load: LoadContextFn<D>,
298}
299
300type LoadContextFn<D> =
302 Arc<dyn Fn(Context<D>, String) -> BoxFuture<'static, Result<String, Error>> + Send + Sync>;
303
304impl<D> Inner<D> {
305 fn route(&self, path: &str) -> Option<(usize, Values)> {
308 self.entries
309 .iter()
310 .enumerate()
311 .filter_map(|(index, entry)| Some((index, entry.template.matches(path)?)))
312 .max_by(|(a, _), (b, _)| {
313 self.entries[*a]
314 .template
315 .specificity(&self.entries[*b].template)
316 })
317 }
318}
319
320pub struct Federation<D> {
322 inner: Arc<Inner<D>>,
323}
324
325impl<D> Clone for Federation<D> {
326 fn clone(&self) -> Self {
327 Self {
328 inner: self.inner.clone(),
329 }
330 }
331}
332
333fn boxed<A, T, E, F, Fut>(
335 f: F,
336) -> Arc<dyn Fn(A) -> BoxFuture<'static, Result<T, Error>> + Send + Sync>
337where
338 F: Fn(A) -> Fut + Send + Sync + 'static,
339 Fut: Future<Output = Result<T, E>> + Send + 'static,
340 E: Into<Error>,
341{
342 Arc::new(move |a| {
343 let future = f(a);
344 Box::pin(async move { future.await.map_err(Into::into) })
345 })
346}
347
348pub struct Builder<D> {
350 origin: Option<OriginRule<D>>,
351 entries: Vec<Entry<D>>,
352 authorize: Vec<(String, AuthorizeFn<D>)>,
353 key_pairs: Option<KeysFn<D>>,
354 handle: Option<LookupFn<D, String>>,
355 map_alias: Option<LookupFn<D, Url>>,
356 webfinger_links: Option<LinksFn<D>>,
357 nodeinfo: Option<NodeInfoFn<D>>,
358 signed_fetch: Option<signer::SignedFetch<D>>,
359 on_error: Option<ErrorFn>,
360 inboxes: Vec<(String, String)>,
361 shared_inbox: Option<String>,
362 aliases: Vec<(String, &'static str, String)>,
363 listeners: std::collections::HashMap<&'static str, inbox::ListenerFn<D>>,
364 fallback_listener: Option<inbox::ListenerFn<D>>,
365 blocked: Option<inbox::BlockedFn<D>>,
366 on_unverified: Option<inbox::UnverifiedFn<D>>,
367 inbox_queue: Option<inbox::QueueFn<D>>,
368 gateway: Option<GatewayFn<D>>,
369 gateway_media: Option<GatewayMediaFn<D>>,
370 gateway_inbox: Option<GatewayInboxFn<D>>,
371 forward: Option<inbox::ForwardFn<D>>,
372 read_as_written: bool,
373 linked_data_first: bool,
374 remote_contexts: Option<RemoteContexts<D>>,
375 errors: Vec<String>,
376}
377
378impl<D: Clone + Send + Sync + 'static> Federation<D> {
379 #[must_use]
380 pub fn builder() -> Builder<D> {
381 Builder {
382 origin: None,
383 entries: Vec::new(),
384 authorize: Vec::new(),
385 key_pairs: None,
386 handle: None,
387 map_alias: None,
388 webfinger_links: None,
389 nodeinfo: None,
390 signed_fetch: None,
391 on_error: None,
392 inboxes: Vec::new(),
393 shared_inbox: None,
394 aliases: Vec::new(),
395 listeners: std::collections::HashMap::new(),
396 fallback_listener: None,
397 blocked: None,
398 on_unverified: None,
399 inbox_queue: None,
400 gateway: None,
401 gateway_media: None,
402 gateway_inbox: None,
403 forward: None,
404 read_as_written: false,
405 linked_data_first: false,
406 remote_contexts: None,
407 errors: Vec::new(),
408 }
409 }
410
411 #[must_use]
415 pub fn uris(&self, origin: Url) -> Uris {
416 Uris {
417 templates: self.inner.templates.clone(),
418 origin,
419 }
420 }
421
422 #[must_use]
425 pub fn context(&self, origin: Url, data: D) -> Context<D> {
426 Context::new(self.inner.clone(), data, origin, None)
427 }
428
429 fn origin_for(&self, host: &str, data: &D) -> Option<Url> {
431 match &self.inner.origin {
432 OriginRule::Fixed(origin) => Some(origin.clone()),
433 OriginRule::PerHost(origin) => origin(host, data),
434 }
435 }
436
437 fn inbox_at(&self, path: &str) -> Option<Option<ActorRef>> {
440 if self
441 .inner
442 .shared_inbox
443 .as_ref()
444 .is_some_and(|shared| shared.matches(path).is_some())
445 {
446 return Some(None);
447 }
448 self.inner.inboxes.iter().find_map(|(kind, template)| {
449 let values = template.matches(path)?;
450 Some(Some(ActorRef::new(
451 kind.clone(),
452 values.single().unwrap_or_default(),
453 )))
454 })
455 }
456
457 #[must_use]
463 pub fn is_inbox(&self, path: &str) -> bool {
464 self.inbox_at(path).is_some()
465 || (self.inner.gateway_inbox.is_some() && is_gateway_path(path))
466 }
467
468 async fn gateway(&self, request: &http::request::Parts, body: &[u8], data: D) -> Handled {
471 let host = request_host(request);
472 let Some(origin) = self.origin_for(&host, &data) else {
473 return Handled::NotFound;
474 };
475 if let Some(hashlink) = request
476 .uri
477 .path()
478 .strip_prefix(crate::portable::GATEWAY_PATH)
479 .filter(|rest| rest.starts_with("hl:"))
480 {
481 let context = Context::new(
482 self.inner.clone(),
483 data,
484 origin,
485 Some(RequestInfo::of(request, host)),
486 );
487 return Handled::Response(self.media(context, request, hashlink).await);
488 }
489 let compatible = format!(
490 "{}{}",
491 origin.as_str().trim_end_matches('/'),
492 request.uri.path()
493 );
494 let Some(uri) = ApUri::parse(&compatible) else {
495 return Handled::NotFound;
496 };
497 let context = Context::new(
498 self.inner.clone(),
499 data,
500 origin,
501 Some(RequestInfo::of(request, host)),
502 );
503 let head = request.method == Method::HEAD;
504 let response = if request.method == Method::GET || head {
505 let Some(load) = &self.inner.gateway else {
506 return Handled::NotFound;
507 };
508 match load(context.clone(), uri.clone()).await {
509 Ok(Found::Found(document)) => match servable(&context, &document, &uri).await {
510 Ok(()) => without_body_if(head, portable_found(Found::Found(document), &uri)),
511 Err(refused) => {
512 context.report(&refused.to_string().into());
513 empty(refused.status())
514 }
515 },
516 Ok(found) => without_body_if(head, portable_found(found, &uri)),
517 Err(error) => {
518 context.report(&error);
519 empty(StatusCode::INTERNAL_SERVER_ERROR)
520 }
521 }
522 } else if request.method == Method::POST {
523 let Some(inbox_for) = &self.inner.gateway_inbox else {
524 return Handled::Response(method_not_allowed());
525 };
526 match inbox_for(context.clone(), uri.clone()).await {
527 Ok(Some(gateway)) => inbox::receive_at_gateway(context, uri, gateway, body).await,
528 Ok(None) => empty(StatusCode::NOT_FOUND),
530 Err(error) => {
531 context.report(&error);
532 empty(StatusCode::INTERNAL_SERVER_ERROR)
533 }
534 }
535 } else {
536 let mut response = empty(StatusCode::METHOD_NOT_ALLOWED);
537 response
538 .headers_mut()
539 .insert(header::ALLOW, HeaderValue::from_static("GET, HEAD, POST"));
540 response
541 };
542 Handled::Response(response)
543 }
544
545 async fn media(
548 &self,
549 context: Context<D>,
550 request: &http::request::Parts,
551 hashlink: &str,
552 ) -> http::Response<Vec<u8>> {
553 let head = request.method == Method::HEAD;
554 if request.method != Method::GET && !head {
555 return method_not_allowed();
556 }
557 let (Some(load), Some(hashlink)) = (&self.inner.gateway_media, Hashlink::parse(hashlink))
558 else {
559 return empty(StatusCode::NOT_FOUND);
560 };
561 match load(context.clone(), hashlink.clone()).await {
562 Ok(Some(media)) if hashlink.matches(&media.bytes) => {
565 let mut response = response(StatusCode::OK, &media.content_type, media.bytes);
566 response.headers_mut().insert(
568 header::CACHE_CONTROL,
569 HeaderValue::from_static("public, max-age=31536000, immutable"),
570 );
571 without_body_if(head, response)
572 }
573 Ok(Some(_)) => {
574 context.report(
575 &format!("gateway: media found for {hashlink} is not what it names").into(),
576 );
577 empty(StatusCode::INTERNAL_SERVER_ERROR)
578 }
579 Ok(None) => empty(StatusCode::NOT_FOUND),
580 Err(error) => {
581 context.report(&error);
582 empty(StatusCode::INTERNAL_SERVER_ERROR)
583 }
584 }
585 }
586
587 pub async fn handle_with_body(
590 &self,
591 request: &http::request::Parts,
592 body: &[u8],
593 data: D,
594 ) -> Handled {
595 if self.serves_gateway() && is_gateway_path(request.uri.path()) {
596 return self.gateway(request, body, data).await;
597 }
598 let Some(recipient) = self.inbox_at(request.uri.path()) else {
599 return self.handle(request, data).await;
600 };
601 let host = request_host(request);
602 let Some(origin) = self.origin_for(&host, &data) else {
603 return Handled::NotFound;
604 };
605 if request.method != Method::POST {
606 let mut response = empty(StatusCode::METHOD_NOT_ALLOWED);
607 response
608 .headers_mut()
609 .insert(header::ALLOW, HeaderValue::from_static("POST"));
610 return Handled::Response(response);
611 }
612 let context = Context::new(
613 self.inner.clone(),
614 data,
615 origin,
616 Some(RequestInfo::of(request, host)),
617 );
618 Handled::Response(inbox::receive(context, recipient, body).await)
619 }
620
621 pub async fn handle(&self, request: &http::request::Parts, data: D) -> Handled {
624 let path = request.uri.path();
625 if self.serves_gateway() && is_gateway_path(path) {
626 return self.gateway(request, b"", data).await;
627 }
628 if self.is_inbox(path) {
629 return Box::pin(self.handle_with_body(request, b"", data)).await;
630 }
631 let special = self.special(path);
632 let matched = if special.is_some() {
633 None
634 } else {
635 let found = self.inner.route(path);
636 if found.is_none() {
637 return Handled::NotFound;
638 }
639 found
640 };
641
642 let host = request_host(request);
643 let Some(origin) = self.origin_for(&host, &data) else {
644 return Handled::NotFound;
645 };
646 let head = request.method == Method::HEAD;
647 let alias = matched
650 .as_ref()
651 .is_some_and(|(index, _)| self.inner.entries[*index].alias);
652 if request.method != Method::GET && !head {
653 if alias {
654 return Handled::NotFound;
655 }
656 return Handled::Response(method_not_allowed());
657 }
658
659 if let Some(special) = special {
660 let context = Context::new(
661 self.inner.clone(),
662 data,
663 origin,
664 Some(RequestInfo::of(request, host)),
665 );
666 let response = match special {
667 Special::WebFinger => webfinger::webfinger(&context, request.uri.query()).await,
668 Special::HostMeta => webfinger::host_meta(&context),
669 Special::NodeInfoLinks => nodeinfo::links(&context),
670 Special::NodeInfo(version) => nodeinfo::document(&context, version).await,
671 };
672 return Handled::Response(without_body_if(head, response));
673 }
674
675 let Some((index, values)) = matched else {
676 return Handled::NotFound;
677 };
678 let accept = request
679 .headers
680 .get(header::ACCEPT)
681 .and_then(|value| value.to_str().ok());
682 if !negotiate::wants_activity(accept) {
683 return Handled::NotAcceptable;
684 }
685 let context = Context::new(
686 self.inner.clone(),
687 data,
688 origin,
689 Some(RequestInfo::of(request, host)),
690 );
691 let entry = &self.inner.entries[index];
692 let response = self
693 .dispatch(&context, entry, values, request.uri.query())
694 .await
695 .unwrap_or_else(|error| {
696 context.report(&error);
697 empty(StatusCode::INTERNAL_SERVER_ERROR)
698 });
699 if entry.alias && response.status() == StatusCode::NOT_FOUND {
702 return Handled::NotFound;
703 }
704 let response = context.with_response_headers(response);
705 Handled::Response(without_body_if(head, response))
706 }
707
708 async fn dispatch(
709 &self,
710 context: &Context<D>,
711 entry: &Entry<D>,
712 values: Values,
713 query: Option<&str>,
714 ) -> Result<http::Response<Vec<u8>>, Error> {
715 let url = context.request_url();
716 if let Some(authorize) = &entry.authorize {
717 match authorize(context.clone(), values.clone()).await? {
718 Access::Allow => {}
719 Access::Unauthorized => {
720 return Ok(unauthorized(context.signer().await.is_some()));
721 }
722 Access::Forbidden => return Ok(forbidden()),
723 Access::NotFound => return Ok(found(Found::NotFound, &url)),
724 }
725 }
726 match &entry.dispatcher {
727 Dispatcher::Actor(load) => {
728 let identifier = values.single().unwrap_or_default().to_owned();
729 Ok(found(load(context.clone(), identifier).await?, &url))
730 }
731 Dispatcher::Object(load) => {
732 let url = if entry.alias {
734 let values: Vec<(&str, &str)> = values.iter().collect();
735 context.object_uri(&entry.kind, &values).unwrap_or(url)
736 } else {
737 url
738 };
739 Ok(found(load(context.clone(), values).await?, &url))
740 }
741 Dispatcher::Collection(collection) => {
742 let identifier = values.single().unwrap_or_default().to_owned();
743 collection
744 .serve(context, &entry.kind, &identifier, query)
745 .await
746 }
747 }
748 }
749
750 fn serves_gateway(&self) -> bool {
751 self.inner.gateway.is_some()
752 || self.inner.gateway_media.is_some()
753 || self.inner.gateway_inbox.is_some()
754 }
755
756 fn special(&self, path: &str) -> Option<Special> {
757 let serves_webfinger = self
758 .inner
759 .entries
760 .iter()
761 .any(|entry| matches!(entry.dispatcher, Dispatcher::Actor(_)));
762 match path {
763 webfinger::WEBFINGER_PATH if serves_webfinger => Some(Special::WebFinger),
764 webfinger::HOST_META_PATH if serves_webfinger => Some(Special::HostMeta),
765 nodeinfo::LINKS_PATH if self.inner.nodeinfo.is_some() => Some(Special::NodeInfoLinks),
766 nodeinfo::PATH_2_0 if self.inner.nodeinfo.is_some() => {
767 Some(Special::NodeInfo(nodeinfo::Version::V2_0))
768 }
769 nodeinfo::PATH_2_1 if self.inner.nodeinfo.is_some() => {
770 Some(Special::NodeInfo(nodeinfo::Version::V2_1))
771 }
772 _ => None,
773 }
774 }
775}
776
777enum Special {
778 WebFinger,
779 HostMeta,
780 NodeInfoLinks,
781 NodeInfo(nodeinfo::Version),
782}
783
784const RESERVED: [&str; 5] = [
786 webfinger::WEBFINGER_PATH,
787 webfinger::HOST_META_PATH,
788 nodeinfo::LINKS_PATH,
789 nodeinfo::PATH_2_0,
790 nodeinfo::PATH_2_1,
791];
792
793impl<D: Clone + Send + Sync + 'static> Builder<D> {
794 #[must_use]
797 pub fn origin(mut self, origin: Url) -> Self {
798 self.origin = Some(OriginRule::Fixed(origin));
799 self
800 }
801
802 #[must_use]
806 pub fn origin_with(
807 mut self,
808 origin: impl Fn(&str, &D) -> Option<Url> + Send + Sync + 'static,
809 ) -> Self {
810 self.origin = Some(OriginRule::PerHost(Arc::new(origin)));
811 self
812 }
813
814 fn add(&mut self, kind: &str, template: &str, dispatcher: Dispatcher<D>) {
815 match Template::parse(template) {
816 Ok(template) => self.entries.push(Entry {
817 kind: kind.to_owned(),
818 template,
819 dispatcher,
820 authorize: None,
821 alias: false,
822 }),
823 Err(error) => self.errors.push(error.to_string()),
824 }
825 }
826
827 #[must_use]
830 pub fn actor<F, Fut, E>(mut self, kind: &str, template: &str, load: F) -> Self
831 where
832 F: Fn(Context<D>, String) -> Fut + Send + Sync + 'static,
833 Fut: Future<Output = Result<Found<Value>, E>> + Send + 'static,
834 E: Into<Error>,
835 {
836 let load = boxed(move |(context, identifier)| load(context, identifier));
837 self.add(
838 kind,
839 template,
840 Dispatcher::Actor(Arc::new(move |context, identifier| {
841 load((context, identifier))
842 })),
843 );
844 self
845 }
846
847 #[must_use]
850 pub fn object<F, Fut, E>(mut self, kind: &str, template: &str, load: F) -> Self
851 where
852 F: Fn(Context<D>, Values) -> Fut + Send + Sync + 'static,
853 Fut: Future<Output = Result<Found<Value>, E>> + Send + 'static,
854 E: Into<Error>,
855 {
856 let load = boxed(move |(context, values)| load(context, values));
857 self.add(
858 kind,
859 template,
860 Dispatcher::Object(Arc::new(move |context, values| load((context, values)))),
861 );
862 self
863 }
864
865 #[must_use]
873 pub fn object_alias(mut self, kind: &str, template: &str) -> Self {
874 self.aliases
875 .push((kind.to_owned(), "object", template.to_owned()));
876 self
877 }
878
879 #[must_use]
887 pub fn collection_alias(mut self, kind: &str, template: &str) -> Self {
888 self.aliases
889 .push((kind.to_owned(), "collection", template.to_owned()));
890 self
891 }
892
893 #[must_use]
896 pub fn collection(mut self, kind: &str, template: &str, collection: Collection<D>) -> Self {
897 self.add(kind, template, Dispatcher::Collection(collection));
898 self
899 }
900
901 #[must_use]
905 pub fn authorize<F, Fut, E>(self, kind: &str, authorize: F) -> Self
906 where
907 F: Fn(Context<D>, Values, Option<Url>) -> Fut + Send + Sync + 'static,
908 Fut: Future<Output = Result<bool, E>> + Send + 'static,
909 E: Into<Error>,
910 {
911 let authorize = Arc::new(authorize);
912 self.guard(kind, move |context: Context<D>, values| {
913 let authorize = authorize.clone();
914 async move {
915 let signer = context.signer().await;
916 Ok::<_, Error>(
917 if authorize(context, values, signer)
918 .await
919 .map_err(Into::into)?
920 {
921 Access::Allow
922 } else {
923 Access::Unauthorized
924 },
925 )
926 }
927 })
928 }
929
930 #[must_use]
937 pub fn guard<F, Fut, E>(mut self, kind: &str, guard: F) -> Self
938 where
939 F: Fn(Context<D>, Values) -> Fut + Send + Sync + 'static,
940 Fut: Future<Output = Result<Access, E>> + Send + 'static,
941 E: Into<Error>,
942 {
943 let guard = boxed(move |(context, values)| guard(context, values));
944 self.authorize.push((
945 kind.to_owned(),
946 Arc::new(move |context, values| guard((context, values))),
947 ));
948 self
949 }
950
951 #[must_use]
953 pub fn key_pairs<F, Fut, E>(mut self, keys: F) -> Self
954 where
955 F: Fn(Context<D>, ActorRef) -> Fut + Send + Sync + 'static,
956 Fut: Future<Output = Result<Vec<PublicKey>, E>> + Send + 'static,
957 E: Into<Error>,
958 {
959 let keys = boxed(move |(context, actor)| keys(context, actor));
960 self.key_pairs = Some(Arc::new(move |context, actor| keys((context, actor))));
961 self
962 }
963
964 #[must_use]
966 pub fn handle<F, Fut, E>(mut self, lookup: F) -> Self
967 where
968 F: Fn(Context<D>, String) -> Fut + Send + Sync + 'static,
969 Fut: Future<Output = Result<Option<ActorRef>, E>> + Send + 'static,
970 E: Into<Error>,
971 {
972 let lookup = boxed(move |(context, username)| lookup(context, username));
973 self.handle = Some(Arc::new(move |context, username| {
974 lookup((context, username))
975 }));
976 self
977 }
978
979 #[must_use]
982 pub fn map_alias<F, Fut, E>(mut self, lookup: F) -> Self
983 where
984 F: Fn(Context<D>, Url) -> Fut + Send + Sync + 'static,
985 Fut: Future<Output = Result<Option<ActorRef>, E>> + Send + 'static,
986 E: Into<Error>,
987 {
988 let lookup = boxed(move |(context, url)| lookup(context, url));
989 self.map_alias = Some(Arc::new(move |context, url| lookup((context, url))));
990 self
991 }
992
993 #[must_use]
996 pub fn webfinger_links(
997 mut self,
998 links: impl Fn(&Context<D>, &ActorRef, &Value) -> Vec<Value> + Send + Sync + 'static,
999 ) -> Self {
1000 self.webfinger_links = Some(Arc::new(links));
1001 self
1002 }
1003
1004 #[must_use]
1006 pub fn nodeinfo<F, Fut, E>(mut self, nodeinfo: F) -> Self
1007 where
1008 F: Fn(Context<D>) -> Fut + Send + Sync + 'static,
1009 Fut: Future<Output = Result<NodeInfo, E>> + Send + 'static,
1010 E: Into<Error>,
1011 {
1012 self.nodeinfo = Some(boxed(nodeinfo));
1013 self
1014 }
1015
1016 #[must_use]
1020 pub fn signed_fetch<K, F, Fut, E>(
1021 mut self,
1022 fetcher: Arc<Fetcher>,
1023 kv: K,
1024 key_ttl: Duration,
1025 key: F,
1026 ) -> Self
1027 where
1028 K: KvStore,
1029 F: Fn(Context<D>) -> Fut + Send + Sync + 'static,
1030 Fut: Future<Output = Result<Option<crate::sig::SenderKey>, E>> + Send + 'static,
1031 E: Into<Error>,
1032 {
1033 self.signed_fetch = Some(signer::SignedFetch {
1034 fetcher,
1035 kv: Arc::new(kv),
1036 key_ttl,
1037 key: boxed(key),
1038 fetcher_for: None,
1039 kv_for: None,
1040 known_key: None,
1041 key_fetched: None,
1042 });
1043 self
1044 }
1045
1046 #[must_use]
1050 pub fn fetcher_for(
1051 mut self,
1052 fetcher: impl Fn(&D) -> Arc<Fetcher> + Send + Sync + 'static,
1053 ) -> Self {
1054 match &mut self.signed_fetch {
1055 Some(settings) => settings.fetcher_for = Some(Arc::new(fetcher)),
1056 None => self.errors.push("fetcher_for before signed_fetch".into()),
1057 }
1058 self
1059 }
1060
1061 #[must_use]
1067 pub fn kv_for<K: KvStore>(mut self, kv: impl Fn(&D) -> Arc<K> + Send + Sync + 'static) -> Self {
1068 match &mut self.signed_fetch {
1069 Some(settings) => {
1070 settings.kv_for = Some(Arc::new(move |data: &D| -> Arc<dyn DynKv> { kv(data) }));
1071 }
1072 None => self.errors.push("kv_for before signed_fetch".into()),
1073 }
1074 self
1075 }
1076
1077 #[must_use]
1082 pub fn known_key<F, Fut, E>(mut self, known: F) -> Self
1083 where
1084 F: Fn(Context<D>, String) -> Fut + Send + Sync + 'static,
1085 Fut: Future<Output = Result<Option<KnownKey>, E>> + Send + 'static,
1086 E: Into<Error>,
1087 {
1088 let known = boxed(move |(context, key_id)| known(context, key_id));
1089 match &mut self.signed_fetch {
1090 Some(settings) => {
1091 settings.known_key =
1092 Some(Arc::new(move |context, key_id| known((context, key_id))));
1093 }
1094 None => self.errors.push("known_key before signed_fetch".into()),
1095 }
1096 self
1097 }
1098
1099 #[must_use]
1104 pub fn key_fetched<F, Fut>(mut self, hook: F) -> Self
1105 where
1106 F: Fn(Context<D>, Value) -> Fut + Send + Sync + 'static,
1107 Fut: Future<Output = ()> + Send + 'static,
1108 {
1109 match &mut self.signed_fetch {
1110 Some(settings) => {
1111 settings.key_fetched = Some(Arc::new(move |context, document| {
1112 Box::pin(hook(context, document))
1113 }));
1114 }
1115 None => self.errors.push("key_fetched before signed_fetch".into()),
1116 }
1117 self
1118 }
1119
1120 #[must_use]
1124 pub fn on_any<F, Fut, E>(mut self, listen: F) -> Self
1125 where
1126 F: Fn(Context<D>, Received<ojak_vocab::generated::AnyObject>) -> Fut
1127 + Send
1128 + Sync
1129 + 'static,
1130 Fut: Future<Output = Result<(), E>> + Send + 'static,
1131 E: Into<Error>,
1132 {
1133 self.fallback_listener = Some(inbox::any_listener(listen));
1134 self
1135 }
1136
1137 #[must_use]
1142 pub fn inbox(mut self, kind: &str, template: &str) -> Self {
1143 self.inboxes.push((kind.to_owned(), template.to_owned()));
1144 self
1145 }
1146
1147 #[must_use]
1149 pub fn shared_inbox(mut self, path: &str) -> Self {
1150 self.shared_inbox = Some(path.to_owned());
1151 self
1152 }
1153
1154 #[must_use]
1158 pub fn on<T, F, Fut, E>(mut self, listen: F) -> Self
1159 where
1160 T: ojak_vocab::json::Typed
1161 + ojak_vocab::json::FromJson
1162 + ojak_vocab::json::ToJson
1163 + Send
1164 + 'static,
1165 F: Fn(Context<D>, Received<T>) -> Fut + Send + Sync + 'static,
1166 Fut: Future<Output = Result<(), E>> + Send + 'static,
1167 E: Into<Error>,
1168 {
1169 if self
1170 .listeners
1171 .insert(T::TYPE, inbox::listener(listen))
1172 .is_some()
1173 {
1174 self.errors.push(format!("two listeners for {}", T::TYPE));
1175 }
1176 self
1177 }
1178
1179 #[must_use]
1190 pub fn read_inbox_as_written(mut self) -> Self {
1191 self.read_as_written = true;
1192 self
1193 }
1194
1195 #[must_use]
1209 pub fn linked_data_before_proof(mut self) -> Self {
1210 self.linked_data_first = true;
1211 self
1212 }
1213
1214 #[must_use]
1222 pub fn blocked<F, Fut, E>(mut self, blocked: F) -> Self
1223 where
1224 F: Fn(Context<D>, String) -> Fut + Send + Sync + 'static,
1225 Fut: Future<Output = Result<bool, E>> + Send + 'static,
1226 E: Into<Error>,
1227 {
1228 let blocked = boxed(move |(context, host)| blocked(context, host));
1229 self.blocked = Some(Arc::new(move |context, host| blocked((context, host))));
1230 self
1231 }
1232
1233 #[must_use]
1244 pub fn remote_contexts<F, Fut, E>(mut self, limits: crate::contexts::Limits, load: F) -> Self
1245 where
1246 F: Fn(Context<D>, String) -> Fut + Send + Sync + 'static,
1247 Fut: Future<Output = Result<String, E>> + Send + 'static,
1248 E: Into<Error>,
1249 {
1250 let load = boxed(move |(context, iri)| load(context, iri));
1251 self.remote_contexts = Some(RemoteContexts {
1252 limits,
1253 load: Arc::new(move |context, iri| load((context, iri))),
1254 });
1255 self
1256 }
1257
1258 #[must_use]
1262 pub fn on_unverified<F, Fut>(mut self, hook: F) -> Self
1263 where
1264 F: Fn(Context<D>, Value) -> Fut + Send + Sync + 'static,
1265 Fut: Future<Output = ()> + Send + 'static,
1266 {
1267 self.on_unverified = Some(Arc::new(move |context, document| {
1268 Box::pin(hook(context, document))
1269 }));
1270 self
1271 }
1272
1273 #[must_use]
1278 pub fn inbox_queue(
1279 mut self,
1280 queue: impl Fn(&D) -> Option<crate::queue::SharedQueue> + Send + Sync + 'static,
1281 ) -> Self {
1282 self.inbox_queue = Some(Arc::new(queue));
1283 self
1284 }
1285
1286 #[must_use]
1299 pub fn gateway<F, Fut, E>(mut self, load: F) -> Self
1300 where
1301 F: Fn(Context<D>, ApUri) -> Fut + Send + Sync + 'static,
1302 Fut: Future<Output = Result<Found<Value>, E>> + Send + 'static,
1303 E: Into<Error>,
1304 {
1305 let load = boxed(move |(context, uri)| load(context, uri));
1306 self.gateway = Some(Arc::new(move |context, uri| load((context, uri))));
1307 self
1308 }
1309
1310 #[must_use]
1317 pub fn gateway_media<F, Fut, E>(mut self, load: F) -> Self
1318 where
1319 F: Fn(Context<D>, Hashlink) -> Fut + Send + Sync + 'static,
1320 Fut: Future<Output = Result<Option<Media>, E>> + Send + 'static,
1321 E: Into<Error>,
1322 {
1323 let load = boxed(move |(context, hashlink)| load(context, hashlink));
1324 self.gateway_media = Some(Arc::new(move |context, hashlink| load((context, hashlink))));
1325 self
1326 }
1327
1328 #[must_use]
1334 pub fn gateway_inbox<F, Fut, E>(mut self, inbox_for: F) -> Self
1335 where
1336 F: Fn(Context<D>, ApUri) -> Fut + Send + Sync + 'static,
1337 Fut: Future<Output = Result<Option<GatewayInbox>, E>> + Send + 'static,
1338 E: Into<Error>,
1339 {
1340 let inbox_for = boxed(move |(context, uri)| inbox_for(context, uri));
1341 self.gateway_inbox = Some(Arc::new(move |context, uri| inbox_for((context, uri))));
1342 self
1343 }
1344
1345 #[must_use]
1352 pub fn forward<F, Fut, E>(mut self, forward: F) -> Self
1353 where
1354 F: Fn(Context<D>, Forward) -> Fut + Send + Sync + 'static,
1355 Fut: Future<Output = Result<(), E>> + Send + 'static,
1356 E: Into<Error>,
1357 {
1358 let forward = boxed(move |(context, activity)| forward(context, activity));
1359 self.forward = Some(Arc::new(move |context, activity| {
1360 forward((context, activity))
1361 }));
1362 self
1363 }
1364
1365 #[must_use]
1367 pub fn on_error(mut self, report: impl Fn(&Error) + Send + Sync + 'static) -> Self {
1368 self.on_error = Some(Arc::new(report));
1369 self
1370 }
1371
1372 pub fn build(mut self) -> Result<Federation<D>, BuildError> {
1381 let mut errors = std::mem::take(&mut self.errors);
1382 let origin = self.origin.take();
1383 if origin.is_none() {
1384 errors.push("no origin: call origin or origin_with".into());
1385 }
1386 for (kind, noun, template) in std::mem::take(&mut self.aliases) {
1387 let template = match Template::parse(&template) {
1388 Ok(template) => template,
1389 Err(error) => {
1390 errors.push(error.to_string());
1391 continue;
1392 }
1393 };
1394 let Some(primary) = self
1395 .entries
1396 .iter()
1397 .find(|entry| entry.kind == kind && entry.noun() == noun && !entry.alias)
1398 else {
1399 errors.push(format!(
1400 "{noun}_alias for {kind:?}, which is no {noun} kind"
1401 ));
1402 continue;
1403 };
1404 let problem = match &primary.dispatcher {
1405 Dispatcher::Object(_) => primary
1407 .template
1408 .names()
1409 .find(|name| !template.names().any(|alias| alias == *name))
1410 .map(|missing| {
1411 format!(
1412 "object_alias {} does not name {missing}, which {} does",
1413 template.as_str(),
1414 primary.template.as_str()
1415 )
1416 }),
1417 _ => (template.names().count() != primary.template.names().count()).then(|| {
1419 format!(
1420 "collection_alias {} has to name its owner as {} does",
1421 template.as_str(),
1422 primary.template.as_str()
1423 )
1424 }),
1425 };
1426 if let Some(problem) = problem {
1427 errors.push(problem);
1428 continue;
1429 }
1430 let dispatcher = match &primary.dispatcher {
1431 Dispatcher::Object(load) => Dispatcher::Object(load.clone()),
1432 Dispatcher::Collection(collection) => Dispatcher::Collection(collection.clone()),
1433 Dispatcher::Actor(load) => Dispatcher::Actor(load.clone()),
1434 };
1435 self.entries.push(Entry {
1436 kind,
1437 template,
1438 dispatcher,
1439 authorize: None,
1440 alias: true,
1441 });
1442 }
1443 for (index, entry) in self.entries.iter().enumerate() {
1444 let expressions = entry.template.names().count();
1445 if !matches!(entry.dispatcher, Dispatcher::Object(_)) && expressions > 1 {
1446 errors.push(format!(
1447 "{} {:?}: {} has more than one expression",
1448 entry.noun(),
1449 entry.kind,
1450 entry.template.as_str()
1451 ));
1452 }
1453 for reserved in RESERVED {
1454 if entry.template.matches(reserved).is_some() {
1455 errors.push(format!(
1456 "{} claims {reserved}, which Ojak serves",
1457 entry.template.as_str()
1458 ));
1459 }
1460 }
1461 for other in &self.entries[index + 1..] {
1462 if entry.kind == other.kind && !entry.alias && !other.alias {
1463 errors.push(format!("two dispatchers of kind {:?}", entry.kind));
1464 }
1465 if entry.template.overlaps(&other.template)
1468 && entry.template.specificity(&other.template).is_eq()
1469 {
1470 errors.push(format!(
1471 "{} and {} could match one path",
1472 entry.template.as_str(),
1473 other.template.as_str()
1474 ));
1475 }
1476 }
1477 }
1478 let mut inboxes = Vec::new();
1479 for (kind, template) in std::mem::take(&mut self.inboxes) {
1480 match Template::parse(&template) {
1481 Ok(template) => {
1482 match self.entries.iter().find(|entry| {
1486 entry.kind == kind && matches!(entry.dispatcher, Dispatcher::Actor(_))
1487 }) {
1488 Some(actor) => {
1489 let expected = actor.template.names().count().min(1);
1490 if template.names().count() != expected {
1491 errors.push(format!(
1492 "inbox {}: has to name its actor in as many expressions \
1493 as its actor's template does",
1494 template.as_str()
1495 ));
1496 }
1497 }
1498 None => {
1499 errors.push(format!("inbox for {kind:?}, which is no actor kind"));
1500 }
1501 }
1502 inboxes.push((kind, template));
1503 }
1504 Err(error) => errors.push(error.to_string()),
1505 }
1506 }
1507 let shared_inbox = match self.shared_inbox.take().map(|path| Template::parse(&path)) {
1508 Some(Ok(template)) if template.names().count() == 0 => Some(template),
1509 Some(Ok(template)) => {
1510 errors.push(format!(
1511 "shared inbox {} has an expression",
1512 template.as_str()
1513 ));
1514 None
1515 }
1516 Some(Err(error)) => {
1517 errors.push(error.to_string());
1518 None
1519 }
1520 None => None,
1521 };
1522 let inbox_templates: Vec<&Template> = inboxes
1523 .iter()
1524 .map(|(_, template)| template)
1525 .chain(shared_inbox.as_ref())
1526 .collect();
1527 for (n, template) in inbox_templates.iter().enumerate() {
1528 for other in self
1529 .entries
1530 .iter()
1531 .map(|entry| &entry.template)
1532 .chain(inbox_templates[n + 1..].iter().copied())
1533 {
1534 if template.overlaps(other) {
1535 errors.push(format!(
1536 "{} and {} could match one path",
1537 template.as_str(),
1538 other.as_str()
1539 ));
1540 }
1541 }
1542 for reserved in RESERVED {
1543 if template.matches(reserved).is_some() {
1544 errors.push(format!(
1545 "{} claims {reserved}, which Ojak serves",
1546 template.as_str()
1547 ));
1548 }
1549 }
1550 }
1551 if !inbox_templates.is_empty() && self.signed_fetch.is_none() {
1552 errors.push("an inbox needs signed_fetch, to fetch and cache senders' keys".into());
1553 }
1554 for (kind, authorize) in std::mem::take(&mut self.authorize) {
1555 let mut found = false;
1556 for entry in self.entries.iter_mut().filter(|entry| entry.kind == kind) {
1557 entry.authorize = Some(authorize.clone());
1558 found = true;
1559 }
1560 if !found {
1561 errors.push(format!("authorize names no dispatcher of kind {kind:?}"));
1562 }
1563 }
1564 let templates = Arc::new(Templates {
1565 routes: self
1566 .entries
1567 .iter()
1568 .filter(|entry| !entry.alias)
1569 .map(|entry| (entry.kind.clone(), entry.noun(), entry.template.clone()))
1570 .collect(),
1571 inboxes: inboxes.clone(),
1572 shared_inbox: shared_inbox.clone(),
1573 });
1574 match origin {
1575 Some(origin) if errors.is_empty() => Ok(Federation {
1576 inner: Arc::new(Inner {
1577 origin,
1578 entries: self.entries,
1579 templates,
1580 key_pairs: self.key_pairs,
1581 handle: self.handle,
1582 map_alias: self.map_alias,
1583 webfinger_links: self.webfinger_links,
1584 nodeinfo: self.nodeinfo,
1585 signed_fetch: self.signed_fetch,
1586 on_error: self.on_error,
1587 inboxes,
1588 shared_inbox,
1589 listeners: self.listeners,
1590 fallback_listener: self.fallback_listener,
1591 blocked: self.blocked,
1592 on_unverified: self.on_unverified,
1593 inbox_queue: self.inbox_queue,
1594 gateway: self.gateway,
1595 gateway_media: self.gateway_media,
1596 gateway_inbox: self.gateway_inbox,
1597 forward: self.forward,
1598 read_as_written: self.read_as_written,
1599 linked_data_first: self.linked_data_first,
1600 remote_contexts: self.remote_contexts,
1601 }),
1602 }),
1603 _ => Err(BuildError(errors)),
1604 }
1605 }
1606}
1607
1608struct RequestInfo {
1610 method: String,
1611 path_and_query: String,
1612 host: String,
1613 headers: Vec<(String, String)>,
1614}
1615
1616impl RequestInfo {
1617 fn of(request: &http::request::Parts, host: String) -> Self {
1618 Self {
1619 method: request.method.as_str().to_owned(),
1620 path_and_query: request.uri.path_and_query().map_or_else(
1621 || request.uri.path().to_owned(),
1622 |pq| pq.as_str().to_owned(),
1623 ),
1624 host,
1625 headers: request
1626 .headers
1627 .iter()
1628 .filter_map(|(name, value)| {
1629 Some((name.as_str().to_owned(), value.to_str().ok()?.to_owned()))
1630 })
1631 .collect(),
1632 }
1633 }
1634}
1635
1636struct ContextInner<D> {
1637 federation: Arc<Inner<D>>,
1638 data: D,
1639 origin: Url,
1640 request: Option<RequestInfo>,
1641 signer: tokio::sync::OnceCell<Signing>,
1642 response_headers: std::sync::Mutex<Vec<(HeaderName, Option<HeaderValue>)>>,
1645}
1646
1647#[derive(Debug)]
1649struct Templates {
1650 routes: Vec<(String, &'static str, Template)>,
1652 inboxes: Vec<(String, Template)>,
1653 shared_inbox: Option<Template>,
1654}
1655
1656const KEY_FRAGMENT: &str = "main-key";
1659
1660#[derive(Clone, Debug)]
1670pub struct Uris {
1671 templates: Arc<Templates>,
1672 origin: Url,
1673}
1674
1675impl Uris {
1676 #[must_use]
1678 pub fn origin(&self) -> &Url {
1679 &self.origin
1680 }
1681
1682 #[must_use]
1684 pub fn with_origin(&self, origin: Url) -> Self {
1685 Self {
1686 templates: self.templates.clone(),
1687 origin,
1688 }
1689 }
1690
1691 fn template(&self, kind: &str, noun: &str) -> Result<&Template, UriError> {
1692 self.templates
1693 .routes
1694 .iter()
1695 .find_map(|(k, n, template)| (k == kind && *n == noun).then_some(template))
1696 .ok_or_else(|| UriError::UnknownKind(kind.to_owned()))
1697 }
1698
1699 fn uri(&self, template: &Template, values: &Values) -> Result<Url, UriError> {
1700 let path = template.expand(values).map_err(UriError::Template)?;
1701 let mut url = self.origin.clone();
1702 url.set_path(&path);
1703 url.set_query(None);
1704 url.set_fragment(None);
1705 Ok(url)
1706 }
1707
1708 fn single(template: &Template, identifier: &str) -> Values {
1709 template
1710 .names()
1711 .map(|name| (name.to_owned(), identifier.to_owned()))
1712 .collect()
1713 }
1714
1715 pub fn actor_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1722 let template = self.template(kind, "actor")?;
1723 self.uri(template, &Self::single(template, identifier))
1724 }
1725
1726 pub fn key_id(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1734 let mut uri = self.actor_uri(kind, identifier)?;
1735 uri.set_fragment(Some(KEY_FRAGMENT));
1736 Ok(uri)
1737 }
1738
1739 pub fn object_uri(&self, kind: &str, values: &[(&str, &str)]) -> Result<Url, UriError> {
1746 let template = self.template(kind, "object")?;
1747 self.uri(template, &values.iter().copied().collect())
1748 }
1749
1750 pub fn collection_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1756 let template = self.template(kind, "collection")?;
1757 self.uri(template, &Self::single(template, identifier))
1758 }
1759
1760 pub fn inbox_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1767 let template = self
1768 .templates
1769 .inboxes
1770 .iter()
1771 .find_map(|(inbox, template)| (inbox == kind).then_some(template))
1772 .ok_or_else(|| UriError::UnknownKind(kind.to_owned()))?;
1773 self.uri(template, &Self::single(template, identifier))
1774 }
1775
1776 pub fn shared_inbox_uri(&self) -> Result<Url, UriError> {
1782 let template = self
1783 .templates
1784 .shared_inbox
1785 .as_ref()
1786 .ok_or_else(|| UriError::UnknownKind("shared inbox".to_owned()))?;
1787 self.uri(template, &Values::new())
1788 }
1789}
1790
1791pub struct Context<D> {
1794 inner: Arc<ContextInner<D>>,
1795}
1796
1797impl<D> Clone for Context<D> {
1798 fn clone(&self) -> Self {
1799 Self {
1800 inner: self.inner.clone(),
1801 }
1802 }
1803}
1804
1805impl<D: Clone + Send + Sync + 'static> Context<D> {
1806 fn new(federation: Arc<Inner<D>>, data: D, origin: Url, request: Option<RequestInfo>) -> Self {
1807 Self {
1808 inner: Arc::new(ContextInner {
1809 federation,
1810 data,
1811 origin,
1812 request,
1813 signer: tokio::sync::OnceCell::new(),
1814 response_headers: std::sync::Mutex::new(Vec::new()),
1815 }),
1816 }
1817 }
1818
1819 #[must_use]
1821 pub fn data(&self) -> &D {
1822 &self.inner.data
1823 }
1824
1825 #[must_use]
1827 pub fn origin(&self) -> &Url {
1828 &self.inner.origin
1829 }
1830
1831 #[must_use]
1833 pub fn uris(&self) -> Uris {
1834 Uris {
1835 templates: self.inner.federation.templates.clone(),
1836 origin: self.inner.origin.clone(),
1837 }
1838 }
1839
1840 pub fn actor_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1847 self.uris().actor_uri(kind, identifier)
1848 }
1849
1850 pub fn key_id(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1857 self.uris().key_id(kind, identifier)
1858 }
1859
1860 pub fn object_uri(&self, kind: &str, values: &[(&str, &str)]) -> Result<Url, UriError> {
1867 self.uris().object_uri(kind, values)
1868 }
1869
1870 pub fn collection_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1876 self.uris().collection_uri(kind, identifier)
1877 }
1878
1879 pub fn inbox_uri(&self, kind: &str, identifier: &str) -> Result<Url, UriError> {
1885 self.uris().inbox_uri(kind, identifier)
1886 }
1887
1888 pub fn shared_inbox_uri(&self) -> Result<Url, UriError> {
1894 self.uris().shared_inbox_uri()
1895 }
1896
1897 #[must_use]
1900 pub fn is_our_host(&self, host: &str) -> bool {
1901 let host = normalize_host(host);
1902 match &self.inner.federation.origin {
1903 OriginRule::Fixed(origin) => authority(origin) == host,
1904 OriginRule::PerHost(origin_for) => {
1905 origin_for(&host, &self.inner.data).as_ref() == Some(&self.inner.origin)
1906 }
1907 }
1908 }
1909
1910 #[must_use]
1913 pub fn parse_uri(&self, iri: &str) -> Option<Route> {
1914 let url = Url::parse(iri).ok()?;
1915 if url.scheme() != self.inner.origin.scheme() || !self.is_our_host(&authority(&url)) {
1916 return None;
1917 }
1918 let (index, values) = self.inner.federation.route(url.path())?;
1919 let entry = &self.inner.federation.entries[index];
1920 Some(match entry.dispatcher {
1921 Dispatcher::Actor(_) => Route::Actor(ActorRef::new(
1922 entry.kind.clone(),
1923 values.single().unwrap_or_default(),
1924 )),
1925 Dispatcher::Object(_) => Route::Object {
1926 kind: entry.kind.clone(),
1927 values,
1928 },
1929 Dispatcher::Collection(_) => Route::Collection {
1930 kind: entry.kind.clone(),
1931 identifier: values.single().unwrap_or_default().to_owned(),
1932 },
1933 })
1934 }
1935
1936 #[must_use]
1939 pub fn parse_actor(&self, kind: &str, iri: &str) -> Option<String> {
1940 match self.parse_uri(iri)? {
1941 Route::Actor(actor) if actor.kind == kind => Some(actor.identifier),
1942 _ => None,
1943 }
1944 }
1945
1946 #[must_use]
1950 pub fn parse_object(&self, kind: &str, iri: &str) -> Option<Values> {
1951 match self.parse_uri(iri)? {
1952 Route::Object {
1953 kind: found,
1954 values,
1955 } if found == kind => Some(values),
1956 _ => None,
1957 }
1958 }
1959
1960 pub async fn actor_keys(&self, actor: &ActorRef) -> Result<Vec<PublicKey>, Error> {
1967 match &self.inner.federation.key_pairs {
1968 Some(keys) => keys(self.clone(), actor.clone()).await,
1969 None => Ok(Vec::new()),
1970 }
1971 }
1972
1973 pub async fn signer(&self) -> Option<Url> {
1979 match self.signing().await {
1980 Signing::Verified(signer) => Some(signer),
1981 _ => None,
1982 }
1983 }
1984
1985 pub async fn signing(&self) -> Signing {
1990 self.inner
1991 .signer
1992 .get_or_init(|| signer::verify(self))
1993 .await
1994 .clone()
1995 }
1996
1997 async fn load_actor(&self, actor: &ActorRef) -> Result<Found<Value>, Error> {
1999 let Some(entry) = self
2000 .inner
2001 .federation
2002 .entries
2003 .iter()
2004 .find(|entry| entry.kind == actor.kind && entry.noun() == "actor")
2005 else {
2006 return Ok(Found::NotFound);
2007 };
2008 match &entry.dispatcher {
2009 Dispatcher::Actor(load) => load(self.clone(), actor.identifier.clone()).await,
2010 _ => Ok(Found::NotFound),
2011 }
2012 }
2013
2014 #[must_use]
2018 pub fn query(&self) -> Option<&str> {
2019 let request = self.inner.request.as_ref()?;
2020 request
2021 .path_and_query
2022 .split_once('?')
2023 .map(|(_, query)| query)
2024 }
2025
2026 #[must_use]
2029 pub fn query_value(&self, name: &str) -> Option<String> {
2030 url::form_urlencoded::parse(self.query()?.as_bytes())
2031 .find(|(key, _)| key == name)
2032 .map(|(_, value)| value.into_owned())
2033 }
2034
2035 #[must_use]
2038 pub fn request_header(&self, name: &str) -> Option<String> {
2039 let request = self.inner.request.as_ref()?;
2040 request
2041 .headers
2042 .iter()
2043 .find(|(header, _)| header.eq_ignore_ascii_case(name))
2044 .map(|(_, value)| value.clone())
2045 }
2046
2047 pub fn set_response_header(&self, name: &str, value: Option<&str>) {
2054 let Ok(name) = HeaderName::try_from(name) else {
2055 return;
2056 };
2057 let value = match value.map(HeaderValue::try_from) {
2058 Some(Ok(value)) => Some(value),
2059 Some(Err(_)) => return,
2060 None => None,
2061 };
2062 if let Ok(mut headers) = self.inner.response_headers.lock() {
2063 headers.push((name, value));
2064 }
2065 }
2066
2067 fn with_response_headers(
2069 &self,
2070 mut response: http::Response<Vec<u8>>,
2071 ) -> http::Response<Vec<u8>> {
2072 if let Ok(headers) = self.inner.response_headers.lock() {
2073 for (name, value) in headers.iter() {
2074 match value {
2075 Some(value) => {
2076 response.headers_mut().insert(name.clone(), value.clone());
2077 }
2078 None => {
2079 response.headers_mut().remove(name);
2080 }
2081 }
2082 }
2083 }
2084 response
2085 }
2086
2087 fn request_url(&self) -> Url {
2089 let mut url = self.inner.origin.clone();
2090 if let Some(request) = &self.inner.request {
2091 let (path, query) = request
2092 .path_and_query
2093 .split_once('?')
2094 .map_or((request.path_and_query.as_str(), None), |(p, q)| {
2095 (p, Some(q))
2096 });
2097 url.set_path(path);
2098 url.set_query(query);
2099 }
2100 url
2101 }
2102
2103 fn report(&self, error: &Error) {
2104 if let Some(report) = &self.inner.federation.on_error {
2105 report(error);
2106 }
2107 }
2108}
2109
2110fn normalize_host(host: &str) -> String {
2112 let host = host.to_ascii_lowercase();
2113 if host.starts_with('[') {
2114 return host;
2116 }
2117 match host.rsplit_once(':') {
2118 Some((name, port)) if port.chars().all(|c| c.is_ascii_digit()) => {
2119 format!("{}:{port}", name.trim_end_matches('.'))
2120 }
2121 _ => host.trim_end_matches('.').to_owned(),
2122 }
2123}
2124
2125fn request_host(request: &http::request::Parts) -> String {
2128 let host = request
2129 .headers
2130 .get(header::HOST)
2131 .and_then(|value| value.to_str().ok())
2132 .or_else(|| request.uri.authority().map(http::uri::Authority::as_str))
2133 .unwrap_or_default();
2134 normalize_host(host)
2135}
2136
2137fn authority(url: &Url) -> String {
2139 let host = url.host_str().unwrap_or_default();
2140 match url.port() {
2141 Some(port) => format!("{host}:{port}"),
2142 None => host.to_owned(),
2143 }
2144}
2145
2146fn response(status: StatusCode, content_type: &str, body: Vec<u8>) -> http::Response<Vec<u8>> {
2147 let mut response = http::Response::new(body);
2148 *response.status_mut() = status;
2149 if let Ok(value) = HeaderValue::from_str(content_type) {
2150 response.headers_mut().insert(header::CONTENT_TYPE, value);
2151 }
2152 response
2153}
2154
2155fn empty(status: StatusCode) -> http::Response<Vec<u8>> {
2156 let mut response = http::Response::new(Vec::new());
2157 *response.status_mut() = status;
2158 response
2159}
2160
2161fn method_not_allowed() -> http::Response<Vec<u8>> {
2162 let mut response = empty(StatusCode::METHOD_NOT_ALLOWED);
2163 response
2164 .headers_mut()
2165 .insert(header::ALLOW, HeaderValue::from_static("GET, HEAD"));
2166 response
2167}
2168
2169fn unauthorized(signed: bool) -> http::Response<Vec<u8>> {
2170 let mut response = empty(StatusCode::UNAUTHORIZED);
2171 if !signed {
2172 response.headers_mut().insert(
2174 header::WWW_AUTHENTICATE,
2175 HeaderValue::from_static("Signature realm=\"ActivityPub\""),
2176 );
2177 }
2178 response
2179 .headers_mut()
2180 .insert(header::VARY, HeaderValue::from_static("Accept, Signature"));
2181 response
2182}
2183
2184fn forbidden() -> http::Response<Vec<u8>> {
2185 let mut response = empty(StatusCode::FORBIDDEN);
2186 response
2187 .headers_mut()
2188 .insert(header::VARY, HeaderValue::from_static("Accept, Signature"));
2189 response
2190}
2191
2192fn without_body_if(head: bool, mut response: http::Response<Vec<u8>>) -> http::Response<Vec<u8>> {
2193 if head {
2194 let length = response.body().len();
2195 response.body_mut().clear();
2196 response
2197 .headers_mut()
2198 .insert(header::CONTENT_LENGTH, HeaderValue::from(length));
2199 }
2200 response
2201}
2202
2203pub(crate) fn activity(status: StatusCode, mut document: Value) -> http::Response<Vec<u8>> {
2205 if let Some(members) = document.as_object_mut()
2206 && !members.contains_key("@context")
2207 {
2208 let mut with_context = serde_json::Map::new();
2209 with_context.insert("@context".into(), default_context());
2210 with_context.append(members);
2211 *members = with_context;
2212 }
2213 let mut response = response(
2214 status,
2215 ACTIVITY_JSON,
2216 serde_json::to_vec(&document).unwrap_or_default(),
2217 );
2218 response
2219 .headers_mut()
2220 .insert(header::VARY, HeaderValue::from_static("Accept"));
2221 response
2222}
2223
2224fn is_gateway_path(path: &str) -> bool {
2226 path.starts_with(crate::portable::GATEWAY_PATH)
2227}
2228
2229pub const PORTABLE_JSON: &str =
2231 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
2232
2233#[derive(Debug)]
2235enum Unservable {
2236 Another { id: Option<String>, uri: String },
2239 Unproven {
2241 uri: String,
2242 why: crate::portable::PortableError,
2243 },
2244}
2245
2246impl Unservable {
2247 fn status(&self) -> StatusCode {
2248 match self {
2249 Self::Another { .. } => StatusCode::NOT_FOUND,
2250 Self::Unproven { .. } => StatusCode::INTERNAL_SERVER_ERROR,
2251 }
2252 }
2253}
2254
2255impl std::fmt::Display for Unservable {
2256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2257 match self {
2258 Self::Another { id: Some(id), uri } => {
2259 write!(f, "gateway: found {id} for {uri}, not serving it")
2260 }
2261 Self::Another { id: None, uri } => {
2262 write!(
2263 f,
2264 "gateway: found a document with no id for {uri}, not serving it"
2265 )
2266 }
2267 Self::Unproven { uri, why } => write!(f, "gateway: not serving {uri}: {why}"),
2268 }
2269 }
2270}
2271
2272async fn servable<D: Clone + Send + Sync + 'static>(
2277 context: &Context<D>,
2278 document: &Value,
2279 uri: &ApUri,
2280) -> Result<(), Unservable> {
2281 let id = document.get("id").and_then(Value::as_str);
2282 if id.and_then(ApUri::parse).as_ref() != Some(uri) {
2283 return Err(Unservable::Another {
2284 id: id.map(str::to_owned),
2285 uri: uri.canonical(),
2286 });
2287 }
2288 if document.get("proof").is_none() && is_collection(document) {
2289 return Ok(());
2290 }
2291 let fetcher = context
2292 .inner
2293 .federation
2294 .signed_fetch
2295 .as_ref()
2296 .map(|settings| settings.fetcher(context.data()));
2297 let resolver = fetcher.as_deref().and_then(crate::fetch::Fetcher::resolver);
2298 crate::portable::verify(document, resolver)
2299 .await
2300 .map(drop)
2301 .map_err(|why| Unservable::Unproven {
2302 uri: uri.canonical(),
2303 why,
2304 })
2305}
2306
2307fn is_collection(document: &Value) -> bool {
2308 const COLLECTIONS: [&str; 4] = [
2309 "Collection",
2310 "OrderedCollection",
2311 "CollectionPage",
2312 "OrderedCollectionPage",
2313 ];
2314 crate::portable::has_type(document, &COLLECTIONS)
2315}
2316
2317fn portable_found(found: Found<Value>, uri: &ApUri) -> http::Response<Vec<u8>> {
2321 match found {
2322 Found::Found(document) => {
2323 let status = if crate::portable::has_type(&document, &["Tombstone"]) {
2324 StatusCode::GONE
2325 } else {
2326 StatusCode::OK
2327 };
2328 let body = serde_json::to_vec(&document).unwrap_or_default();
2329 response(status, PORTABLE_JSON, body)
2330 }
2331 Found::Gone(deleted) => {
2332 let mut tombstone = json!({
2333 "@context": "https://www.w3.org/ns/activitystreams",
2334 "id": uri.canonical(),
2335 "type": "Tombstone",
2336 });
2337 if let Some(deleted) = deleted {
2338 tombstone["deleted"] = deleted.to_rfc3339_opts(SecondsFormat::Secs, true).into();
2339 }
2340 let body = serde_json::to_vec(&tombstone).unwrap_or_default();
2341 response(StatusCode::GONE, PORTABLE_JSON, body)
2342 }
2343 Found::NotFound => empty(StatusCode::NOT_FOUND),
2344 Found::Redirect(location) => redirect(&location),
2345 }
2346}
2347
2348fn redirect(location: &Url) -> http::Response<Vec<u8>> {
2350 let mut response = empty(StatusCode::FOUND);
2351 if let Ok(value) = HeaderValue::from_str(location.as_str()) {
2352 response.headers_mut().insert(header::LOCATION, value);
2353 }
2354 response
2355 .headers_mut()
2356 .insert(header::VARY, HeaderValue::from_static("Accept"));
2357 response
2358}
2359
2360fn found(found: Found<Value>, url: &Url) -> http::Response<Vec<u8>> {
2361 match found {
2362 Found::Found(document) => activity(StatusCode::OK, document),
2363 Found::Gone(deleted) => {
2364 let mut tombstone = json!({
2365 "@context": "https://www.w3.org/ns/activitystreams",
2366 "id": url.as_str(),
2367 "type": "Tombstone",
2368 });
2369 if let Some(deleted) = deleted {
2370 tombstone["deleted"] = deleted.to_rfc3339_opts(SecondsFormat::Secs, true).into();
2371 }
2372 activity(StatusCode::GONE, tombstone)
2373 }
2374 Found::NotFound => {
2375 let mut response = empty(StatusCode::NOT_FOUND);
2376 response
2377 .headers_mut()
2378 .insert(header::VARY, HeaderValue::from_static("Accept"));
2379 response
2380 }
2381 Found::Redirect(location) => redirect(&location),
2382 }
2383}
2384
2385trait DynKv: Send + Sync {
2387 fn get<'a>(&'a self, key: &'a [&'a str]) -> BoxFuture<'a, Result<Option<Value>, KvError>>;
2388 fn set<'a>(
2389 &'a self,
2390 key: &'a [&'a str],
2391 value: Value,
2392 ttl: Option<Duration>,
2393 ) -> BoxFuture<'a, Result<(), KvError>>;
2394 fn insert<'a>(
2395 &'a self,
2396 key: &'a [&'a str],
2397 value: Value,
2398 ttl: Option<Duration>,
2399 ) -> BoxFuture<'a, Result<bool, KvError>>;
2400 fn delete<'a>(&'a self, key: &'a [&'a str]) -> BoxFuture<'a, Result<(), KvError>>;
2401}
2402
2403impl<K: KvStore> DynKv for K {
2404 fn get<'a>(&'a self, key: &'a [&'a str]) -> BoxFuture<'a, Result<Option<Value>, KvError>> {
2405 Box::pin(KvStore::get(self, key))
2406 }
2407
2408 fn set<'a>(
2409 &'a self,
2410 key: &'a [&'a str],
2411 value: Value,
2412 ttl: Option<Duration>,
2413 ) -> BoxFuture<'a, Result<(), KvError>> {
2414 Box::pin(KvStore::set(self, key, value, ttl))
2415 }
2416
2417 fn insert<'a>(
2418 &'a self,
2419 key: &'a [&'a str],
2420 value: Value,
2421 ttl: Option<Duration>,
2422 ) -> BoxFuture<'a, Result<bool, KvError>> {
2423 Box::pin(KvStore::insert(self, key, value, ttl))
2424 }
2425
2426 fn delete<'a>(&'a self, key: &'a [&'a str]) -> BoxFuture<'a, Result<(), KvError>> {
2427 Box::pin(KvStore::delete(self, key))
2428 }
2429}
2430
2431#[cfg(test)]
2432mod tests {
2433 use super::*;
2434
2435 #[test]
2436 fn hosts_are_compared_without_case_or_a_trailing_dot() {
2437 assert_eq!(normalize_host("Oeee.Cafe."), "oeee.cafe");
2438 assert_eq!(normalize_host("oeee.cafe.:8443"), "oeee.cafe:8443");
2439 assert_eq!(normalize_host("127.0.0.1:3000"), "127.0.0.1:3000");
2440 assert_eq!(normalize_host("[::1]:3000"), "[::1]:3000");
2441 assert_eq!(normalize_host("[::1]"), "[::1]");
2442 }
2443
2444 #[test]
2445 fn keys_go_where_each_is_read() {
2446 let mut actor = json!({"id": "https://a.example/users/1", "type": "Person"});
2447 with_keys(
2448 &mut actor,
2449 &[
2450 PublicKey::Multikey {
2451 id: "https://a.example/users/1#ed25519-key".into(),
2452 multibase: "z6Mk".into(),
2453 },
2454 PublicKey::Rsa {
2455 id: "https://a.example/users/1#main-key".into(),
2456 pem: "PEM".into(),
2457 },
2458 ],
2459 );
2460 assert_eq!(
2461 actor["publicKey"],
2462 json!({"id": "https://a.example/users/1#main-key", "owner": "https://a.example/users/1", "publicKeyPem": "PEM"})
2463 );
2464 assert_eq!(
2465 actor["assertionMethod"][0]["controller"],
2466 "https://a.example/users/1"
2467 );
2468 assert_eq!(actor["assertionMethod"][0]["type"], "Multikey");
2469 }
2470}