1use apub_core::{
2 activitypub::PublicKey,
3 repo::{Dereference, Repo},
4 session::Session,
5};
6use apub_privatekey::KeyId;
7use url::{Host, Url};
8
9#[async_trait::async_trait(?Send)]
10pub trait PublicKeyRepo {
11 type Error: std::error::Error;
12
13 async fn store(&self, public_key: &SimplePublicKey) -> Result<(), Self::Error>;
14
15 async fn fetch(&self, key_id: &KeyId) -> Result<Option<SimplePublicKey>, Self::Error>;
16}
17
18pub trait PublicKeyRepoFactory {
19 type PublicKeyRepo: PublicKeyRepo;
20
21 fn public_key_repo(&self) -> Self::PublicKeyRepo;
22}
23
24pub struct PublicKeyClient<LocalRepo, RemoteRepo> {
25 local_host: Host<String>,
26 local_port: Option<u16>,
27 local: LocalRepo,
28 remote: RemoteRepo,
29}
30
31#[derive(Debug, thiserror::Error)]
32pub enum PublicKeyError<E, R> {
33 #[error("Public Key's Owner doesn't match fetched Actor")]
34 OwnerMismatch,
35
36 #[error("{0}")]
37 PublicKeyRepo(#[source] E),
38
39 #[error("{0}")]
40 RemoteRepo(#[source] R),
41}
42
43struct PublicKeyId<'a>(&'a KeyId);
44
45#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
46#[serde(rename_all = "camelCase")]
47pub struct SimplePublicKey {
48 pub id: KeyId,
49 pub owner: Url,
50 pub public_key_pem: String,
51}
52
53#[derive(Clone, Debug, serde::Deserialize)]
54#[serde(untagged)]
55#[serde(rename_all = "camelCase")]
56pub enum PublicKeyResponse {
57 PublicKey(SimplePublicKey),
59
60 Actor {
62 id: Url,
63 public_key: SimplePublicKey,
64 },
65}
66
67impl<LocalRepo, RemoteRepo> PublicKeyClient<LocalRepo, RemoteRepo>
68where
69 LocalRepo: PublicKeyRepo,
70 RemoteRepo: Repo,
71{
72 pub fn new(
73 local: LocalRepo,
74 remote: RemoteRepo,
75 local_host: Host<String>,
76 local_port: Option<u16>,
77 ) -> Self {
78 PublicKeyClient {
79 local_host,
80 local_port,
81 local,
82 remote,
83 }
84 }
85
86 pub async fn find<S: Session>(
87 &self,
88 key_id: &KeyId,
89 session: S,
90 ) -> Result<Option<SimplePublicKey>, PublicKeyError<LocalRepo::Error, RemoteRepo::Error>> {
91 let opt = self
92 .local
93 .fetch(key_id)
94 .await
95 .map_err(PublicKeyError::PublicKeyRepo)?;
96
97 if self.is_local(key_id) {
98 return Ok(opt);
99 }
100
101 let response = self
102 .remote
103 .fetch(PublicKeyId(key_id), session)
104 .await
105 .map_err(PublicKeyError::RemoteRepo)?;
106
107 if let Some(response) = response {
108 let public_key = match response {
109 PublicKeyResponse::Actor { id, public_key } if id == public_key.owner => public_key,
110 PublicKeyResponse::PublicKey(public_key) => public_key,
111 _ => return Err(PublicKeyError::OwnerMismatch),
112 };
113
114 self.local
115 .store(&public_key)
116 .await
117 .map_err(PublicKeyError::PublicKeyRepo)?;
118
119 return Ok(Some(public_key));
120 }
121
122 Ok(None)
123 }
124
125 fn is_local(&self, url: &Url) -> bool {
126 url.host() == Some(borrow_host(&self.local_host)) && url.port() == self.local_port
127 }
128}
129
130fn borrow_host(host: &Host<String>) -> Host<&str> {
131 match host {
132 Host::Ipv4(ip) => Host::Ipv4(*ip),
133 Host::Ipv6(ip) => Host::Ipv6(*ip),
134 Host::Domain(ref domain) => Host::Domain(domain),
135 }
136}
137
138impl<'a> Dereference for PublicKeyId<'a> {
139 type Output = PublicKeyResponse;
140
141 fn url(&self) -> &Url {
142 self.0
143 }
144}
145
146impl PublicKey for SimplePublicKey {
147 fn id(&self) -> &Url {
148 &self.id
149 }
150
151 fn owner(&self) -> &Url {
152 &self.owner
153 }
154
155 fn public_key_pem(&self) -> &str {
156 &self.public_key_pem
157 }
158}