1use apub_core::{
2 activitypub::{Activity, DeliverableObject},
3 ingest::{Authority, Ingest},
4 repo::Repo,
5 session::Session,
6};
7use url::Url;
8
9pub trait InboxType {
10 fn is_shared(&self) -> bool;
11}
12
13#[derive(Clone, Debug)]
15pub struct ValidateAuthority<I> {
16 ingest: I,
17}
18
19#[derive(Clone, Debug)]
22pub struct ValidateInbox<I> {
23 ingest: I,
24}
25
26#[derive(Clone, Debug)]
28pub struct ValidateHosts<I> {
29 ingest: I,
30}
31
32pub fn validate_authority<I>(ingest: I) -> ValidateAuthority<I> {
34 ValidateAuthority { ingest }
35}
36
37pub fn validate_inbox<I>(ingest: I) -> ValidateInbox<I> {
40 ValidateInbox { ingest }
41}
42
43pub fn validate_hosts<I>(ingest: I) -> ValidateHosts<I> {
45 ValidateHosts { ingest }
46}
47
48#[derive(Clone, Debug)]
49pub struct AuthorityError {
50 authority: Authority,
51 actor: Url,
52}
53
54#[derive(Clone, Debug)]
55pub struct InboxError;
56
57#[derive(Clone, Debug)]
58pub struct HostError;
59
60#[async_trait::async_trait(?Send)]
61impl<A, I> Ingest<A> for ValidateAuthority<I>
62where
63 A: Activity + 'static,
64 I: Ingest<A>,
65 I::Error: From<AuthorityError>,
66{
67 type Local = I::Local;
68 type ActorId = I::ActorId;
69 type Error = I::Error;
70
71 fn local_repo(&self) -> &Self::Local {
72 self.ingest.local_repo()
73 }
74
75 fn is_local(&self, url: &Url) -> bool {
76 self.ingest.is_local(url)
77 }
78
79 async fn ingest<R: Repo, S: Session>(
80 &self,
81 authority: Authority,
82 actor_id: Self::ActorId,
83 activity: &A,
84 remote_repo: R,
85 session: S,
86 ) -> Result<(), Self::Error>
87 where
88 Self::Error: From<R::Error>,
89 {
90 let activity_actor = activity.actor_id();
91
92 let valid = match &authority {
93 Authority::Server(url) => {
94 url.host() == activity_actor.host() && url.port() == activity_actor.port()
95 }
96 Authority::Actor(actor) => actor == activity_actor,
97 Authority::None => false,
98 };
99
100 if !valid {
101 return Err(I::Error::from(AuthorityError {
102 authority,
103 actor: activity_actor.clone(),
104 }));
105 }
106
107 self.ingest
108 .ingest(authority, actor_id, activity, remote_repo, session)
109 .await
110 }
111}
112
113#[async_trait::async_trait(?Send)]
114impl<A, I> Ingest<A> for ValidateInbox<I>
115where
116 A: DeliverableObject + 'static,
117 I: Ingest<A>,
118 I::Error: From<InboxError>,
119 I::ActorId: InboxType,
120{
121 type Local = I::Local;
122 type ActorId = I::ActorId;
123 type Error = I::Error;
124
125 fn local_repo(&self) -> &Self::Local {
126 self.ingest.local_repo()
127 }
128
129 fn is_local(&self, url: &Url) -> bool {
130 self.ingest.is_local(url)
131 }
132
133 async fn ingest<R: Repo, S: Session>(
134 &self,
135 authority: Authority,
136 actor_id: Self::ActorId,
137 activity: &A,
138 remote_repo: R,
139 session: S,
140 ) -> Result<(), Self::Error>
141 where
142 Self::Error: From<R::Error>,
143 {
144 if !actor_id.is_shared() && activity.is_public() {
146 return Err(I::Error::from(InboxError));
147 }
148
149 if actor_id.is_shared() && !activity.is_public() {
151 return Err(I::Error::from(InboxError));
152 }
153
154 self.ingest
155 .ingest(authority, actor_id, activity, remote_repo, session)
156 .await
157 }
158}
159
160#[async_trait::async_trait(?Send)]
161impl<A, I> Ingest<A> for ValidateHosts<I>
162where
163 A: Activity + 'static,
164 I: Ingest<A>,
165 I::Error: From<HostError>,
166{
167 type Local = I::Local;
168 type ActorId = I::ActorId;
169 type Error = I::Error;
170
171 fn local_repo(&self) -> &Self::Local {
172 self.ingest.local_repo()
173 }
174
175 fn is_local(&self, url: &Url) -> bool {
176 self.ingest.is_local(url)
177 }
178 async fn ingest<R: Repo, S: Session>(
179 &self,
180 authority: Authority,
181 actor_id: Self::ActorId,
182 activity: &A,
183 remote_repo: R,
184 session: S,
185 ) -> Result<(), Self::Error>
186 where
187 Self::Error: From<R::Error>,
188 {
189 if activity.id().host() != activity.actor_id().host()
190 || activity.id().port() != activity.actor_id().port()
191 {
192 return Err(I::Error::from(HostError));
193 }
194
195 self.ingest
196 .ingest(authority, actor_id, activity, remote_repo, session)
197 .await
198 }
199}
200
201impl std::fmt::Display for AuthorityError {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 write!(
204 f,
205 "Authority '{}' not permitted to act on behalf of actor '{}'",
206 self.authority, self.actor
207 )
208 }
209}
210impl std::error::Error for AuthorityError {}
211
212impl std::fmt::Display for InboxError {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 write!(f, "Object delivered to invalid inbox")
215 }
216}
217impl std::error::Error for InboxError {}
218
219impl std::fmt::Display for HostError {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, "Activity and Actor host do not match")
222 }
223}
224impl std::error::Error for HostError {}