2022-08-31 13:01:04 +00:00
|
|
|
{- This file is part of Vervis.
|
|
|
|
-
|
2023-04-29 10:40:44 +00:00
|
|
|
- Written in 2022, 2023 by fr33domlover <fr33domlover@riseup.net>.
|
2022-08-31 13:01:04 +00:00
|
|
|
-
|
|
|
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
|
|
|
-
|
|
|
|
- The author(s) have dedicated all copyright and related and neighboring
|
|
|
|
- rights to this software to the public domain worldwide. This software is
|
|
|
|
- distributed without any warranty.
|
|
|
|
-
|
|
|
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
|
|
|
- with this software. If not, see
|
|
|
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Vervis.Data.Actor
|
|
|
|
( parseLocalActivityURI
|
2023-06-05 09:43:28 +03:00
|
|
|
, parseLocalActivityURI'
|
2022-09-05 16:19:52 +00:00
|
|
|
, parseActivityURI
|
2023-06-05 09:43:28 +03:00
|
|
|
, parseActivityURI'
|
2022-09-05 16:19:52 +00:00
|
|
|
, activityRoute
|
Improve the AP async HTTP delivery API and per-actor key support
New iteration of the ActivityPub delivery implementation and interface.
Advantages over previous interface:
* When sending a ByteString body, the sender is explicitly passed as a
parameter instead of JSON-parsing it out of the ByteString
* Clear 3 operations provided: Send, Resend and Forward
* Support for per-actor keys
* Actor-type-specific functions (e.g. deliverRemoteDB_D) removed
* Only the most high-level API is exposed to Activity handler code, making
handler code more concise and clear
Also added in this patch:
* Foundation for per-actor key support
* 1 key per actor allowed in DB
* Disabled C2S and S2S handlers now un-exported for clarity
* Audience and capability parsing automatically done for all C2S handlers
* Audience and activity composition automatically done for Vervis.Client
builder functions
Caveats:
* Actor documents still don't link to their per-actor keys; that should be the
last piece to complete per-actor key support
* No moderation and anti-spam tools yet
* Delivery API doesn't yet have good integration of persistence layer, e.g.
activity is separately encoded into bytestring for DB and for HTTP; this will
be improved in the next iteration
* Periodic delivery now done in 3 separate steps, running sequentially; it
simplifies the code, but may be changed for efficiency/robustness in the next
iterations
* Periodic delivery collects per-actor keys in a
1-DB-transaction-for-each-delivery fashion, rather than grabbing them in the
big Esqueleto query (or keeping the signed output in the DB; this isn't done
currently to allow for smooth actor key renewal)
* No support yet in the API for delivery where the actor key has already been
fetched, rather than doing a DB transaction to grab it; such support would be
just an optimization, so it's low-priority, but will be added in later
iterations
2022-10-12 16:50:11 +00:00
|
|
|
, stampRoute
|
|
|
|
, parseStampRoute
|
2022-10-16 11:26:24 +00:00
|
|
|
, localActorID
|
2022-10-25 04:54:56 +00:00
|
|
|
, parseLocalURI
|
2023-04-29 10:40:44 +00:00
|
|
|
, parseFedURIOld
|
2022-10-25 04:54:56 +00:00
|
|
|
, parseLocalActorE
|
2023-11-03 10:56:25 +02:00
|
|
|
, parseLocalActorE'
|
2022-08-31 13:01:04 +00:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
2023-04-29 10:40:44 +00:00
|
|
|
import Control.Concurrent.Chan
|
|
|
|
import Control.Concurrent.STM.TVar
|
|
|
|
import Control.Monad
|
|
|
|
import Control.Monad.IO.Class
|
|
|
|
import Control.Monad.Logger.CallStack
|
2022-08-31 13:01:04 +00:00
|
|
|
import Control.Monad.Trans.Except
|
|
|
|
import Data.Text (Text)
|
2022-10-16 11:26:24 +00:00
|
|
|
import Database.Persist.Types
|
2023-04-29 10:40:44 +00:00
|
|
|
import UnliftIO.Exception (try, SomeException, displayException)
|
|
|
|
|
|
|
|
import qualified Data.HashMap.Strict as HM
|
|
|
|
import qualified Data.Text as T
|
2022-08-31 13:01:04 +00:00
|
|
|
|
|
|
|
import Network.FedURI
|
2023-05-24 22:17:14 +00:00
|
|
|
import Web.Actor
|
2022-09-05 16:19:52 +00:00
|
|
|
import Yesod.ActivityPub
|
2023-04-29 10:40:44 +00:00
|
|
|
import Yesod.Actor
|
2022-08-31 13:01:04 +00:00
|
|
|
import Yesod.FedURI
|
|
|
|
import Yesod.Hashids
|
|
|
|
import Yesod.MonadSite
|
|
|
|
|
2023-06-05 09:43:28 +03:00
|
|
|
import qualified Web.Actor as WA
|
|
|
|
import qualified Web.Actor.Persist as WAP
|
|
|
|
|
2022-08-31 13:01:04 +00:00
|
|
|
import Control.Monad.Trans.Except.Local
|
|
|
|
|
2022-09-05 16:19:52 +00:00
|
|
|
import Vervis.FedURI
|
2022-08-31 13:01:04 +00:00
|
|
|
import Vervis.Foundation
|
|
|
|
import Vervis.Model
|
|
|
|
import Vervis.Recipient
|
|
|
|
|
2023-06-05 09:43:28 +03:00
|
|
|
import qualified Vervis.Actor as VA
|
|
|
|
|
2022-08-31 13:01:04 +00:00
|
|
|
parseLocalActivityURI
|
|
|
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
|
|
|
=> LocalURI
|
|
|
|
-> ExceptT Text m (LocalActorBy Key, LocalActorBy KeyHashid, OutboxItemId)
|
|
|
|
parseLocalActivityURI luAct = do
|
2022-09-05 16:19:52 +00:00
|
|
|
route <- fromMaybeE (decodeRouteLocal luAct) "Not a valid route"
|
2022-08-31 13:01:04 +00:00
|
|
|
(actorHash, outboxItemHash) <-
|
|
|
|
fromMaybeE
|
|
|
|
(parseOutboxItemRoute route)
|
2022-09-05 16:19:52 +00:00
|
|
|
"Valid local route, but not an outbox item route"
|
|
|
|
outboxItemID <- decodeKeyHashidE outboxItemHash "Invalid outbox item hash"
|
|
|
|
actorKey <- unhashLocalActorE actorHash "Invalid actor hash"
|
2022-08-31 13:01:04 +00:00
|
|
|
return (actorKey, actorHash, outboxItemID)
|
|
|
|
where
|
|
|
|
parseOutboxItemRoute (PersonOutboxItemR p i) = Just (LocalActorPerson p, i)
|
|
|
|
parseOutboxItemRoute (GroupOutboxItemR g i) = Just (LocalActorGroup g, i)
|
|
|
|
parseOutboxItemRoute (RepoOutboxItemR r i) = Just (LocalActorRepo r, i)
|
|
|
|
parseOutboxItemRoute (DeckOutboxItemR d i) = Just (LocalActorDeck d, i)
|
|
|
|
parseOutboxItemRoute (LoomOutboxItemR l i) = Just (LocalActorLoom l, i)
|
2023-06-26 22:02:54 +03:00
|
|
|
parseOutboxItemRoute (ProjectOutboxItemR r i) = Just (LocalActorProject r, i)
|
2022-08-31 13:01:04 +00:00
|
|
|
parseOutboxItemRoute _ = Nothing
|
2022-09-05 16:19:52 +00:00
|
|
|
|
2023-06-05 09:43:28 +03:00
|
|
|
parseLocalActivityURI'
|
|
|
|
:: LocalURI
|
|
|
|
-> VA.ActE (LocalActorBy Key, LocalActorBy KeyHashid, OutboxItemId)
|
|
|
|
parseLocalActivityURI' luAct = do
|
|
|
|
route <- fromMaybeE (WA.decodeRouteLocal luAct) "Not a valid route"
|
|
|
|
(actorHash, outboxItemHash) <-
|
|
|
|
fromMaybeE
|
|
|
|
(parseOutboxItemRoute route)
|
|
|
|
"Valid local route, but not an outbox item route"
|
|
|
|
outboxItemID <- WAP.decodeKeyHashidE outboxItemHash "Invalid outbox item hash"
|
|
|
|
actorKey <- VA.unhashLocalActorE actorHash "Invalid actor hash"
|
|
|
|
return (actorKey, actorHash, outboxItemID)
|
|
|
|
where
|
|
|
|
parseOutboxItemRoute (PersonOutboxItemR p i) = Just (LocalActorPerson p, i)
|
|
|
|
parseOutboxItemRoute (GroupOutboxItemR g i) = Just (LocalActorGroup g, i)
|
|
|
|
parseOutboxItemRoute (RepoOutboxItemR r i) = Just (LocalActorRepo r, i)
|
|
|
|
parseOutboxItemRoute (DeckOutboxItemR d i) = Just (LocalActorDeck d, i)
|
|
|
|
parseOutboxItemRoute (LoomOutboxItemR l i) = Just (LocalActorLoom l, i)
|
2023-11-02 23:14:29 +02:00
|
|
|
parseOutboxItemRoute (ProjectOutboxItemR r i) = Just (LocalActorProject r, i)
|
2023-06-05 09:43:28 +03:00
|
|
|
parseOutboxItemRoute _ = Nothing
|
|
|
|
|
2022-09-05 16:19:52 +00:00
|
|
|
-- | If the given URI is remote, return as is. If the URI is local, verify that
|
|
|
|
-- it parses as an activity URI, i.e. an outbox item route, and return the
|
|
|
|
-- parsed route.
|
|
|
|
parseActivityURI
|
|
|
|
:: (MonadSite m, SiteEnv m ~ App)
|
|
|
|
=> FedURI
|
|
|
|
-> ExceptT Text m
|
|
|
|
(Either
|
|
|
|
(LocalActorBy Key, LocalActorBy KeyHashid, OutboxItemId)
|
|
|
|
FedURI
|
|
|
|
)
|
|
|
|
parseActivityURI u@(ObjURI h lu) = do
|
2023-04-29 10:40:44 +00:00
|
|
|
hl <- hostIsLocalOld h
|
2022-09-05 16:19:52 +00:00
|
|
|
if hl
|
|
|
|
then Left <$> parseLocalActivityURI lu
|
|
|
|
else pure $ Right u
|
2023-06-05 09:43:28 +03:00
|
|
|
|
|
|
|
-- | If the given URI is remote, return as is. If the URI is local, verify that
|
|
|
|
-- it parses as an activity URI, i.e. an outbox item route, and return the
|
|
|
|
-- parsed route.
|
|
|
|
parseActivityURI'
|
|
|
|
:: FedURI
|
|
|
|
-> VA.ActE
|
|
|
|
(Either
|
|
|
|
(LocalActorBy Key, LocalActorBy KeyHashid, OutboxItemId)
|
|
|
|
FedURI
|
|
|
|
)
|
|
|
|
parseActivityURI' u@(ObjURI h lu) = do
|
|
|
|
hl <- WA.hostIsLocal h
|
|
|
|
if hl
|
|
|
|
then Left <$> parseLocalActivityURI' lu
|
|
|
|
else pure $ Right u
|
2022-09-05 16:19:52 +00:00
|
|
|
|
|
|
|
activityRoute :: LocalActorBy KeyHashid -> KeyHashid OutboxItem -> Route App
|
|
|
|
activityRoute (LocalActorPerson p) = PersonOutboxItemR p
|
|
|
|
activityRoute (LocalActorGroup g) = GroupOutboxItemR g
|
|
|
|
activityRoute (LocalActorRepo r) = RepoOutboxItemR r
|
|
|
|
activityRoute (LocalActorDeck d) = DeckOutboxItemR d
|
|
|
|
activityRoute (LocalActorLoom l) = LoomOutboxItemR l
|
2023-06-26 22:02:54 +03:00
|
|
|
activityRoute (LocalActorProject r) = ProjectOutboxItemR r
|
Improve the AP async HTTP delivery API and per-actor key support
New iteration of the ActivityPub delivery implementation and interface.
Advantages over previous interface:
* When sending a ByteString body, the sender is explicitly passed as a
parameter instead of JSON-parsing it out of the ByteString
* Clear 3 operations provided: Send, Resend and Forward
* Support for per-actor keys
* Actor-type-specific functions (e.g. deliverRemoteDB_D) removed
* Only the most high-level API is exposed to Activity handler code, making
handler code more concise and clear
Also added in this patch:
* Foundation for per-actor key support
* 1 key per actor allowed in DB
* Disabled C2S and S2S handlers now un-exported for clarity
* Audience and capability parsing automatically done for all C2S handlers
* Audience and activity composition automatically done for Vervis.Client
builder functions
Caveats:
* Actor documents still don't link to their per-actor keys; that should be the
last piece to complete per-actor key support
* No moderation and anti-spam tools yet
* Delivery API doesn't yet have good integration of persistence layer, e.g.
activity is separately encoded into bytestring for DB and for HTTP; this will
be improved in the next iteration
* Periodic delivery now done in 3 separate steps, running sequentially; it
simplifies the code, but may be changed for efficiency/robustness in the next
iterations
* Periodic delivery collects per-actor keys in a
1-DB-transaction-for-each-delivery fashion, rather than grabbing them in the
big Esqueleto query (or keeping the signed output in the DB; this isn't done
currently to allow for smooth actor key renewal)
* No support yet in the API for delivery where the actor key has already been
fetched, rather than doing a DB transaction to grab it; such support would be
just an optimization, so it's low-priority, but will be added in later
iterations
2022-10-12 16:50:11 +00:00
|
|
|
|
|
|
|
stampRoute :: LocalActorBy KeyHashid -> KeyHashid SigKey -> Route App
|
|
|
|
stampRoute (LocalActorPerson p) = PersonStampR p
|
|
|
|
stampRoute (LocalActorGroup g) = GroupStampR g
|
|
|
|
stampRoute (LocalActorRepo r) = RepoStampR r
|
|
|
|
stampRoute (LocalActorDeck d) = DeckStampR d
|
|
|
|
stampRoute (LocalActorLoom l) = LoomStampR l
|
2023-06-26 22:02:54 +03:00
|
|
|
stampRoute (LocalActorProject r) = ProjectStampR r
|
Improve the AP async HTTP delivery API and per-actor key support
New iteration of the ActivityPub delivery implementation and interface.
Advantages over previous interface:
* When sending a ByteString body, the sender is explicitly passed as a
parameter instead of JSON-parsing it out of the ByteString
* Clear 3 operations provided: Send, Resend and Forward
* Support for per-actor keys
* Actor-type-specific functions (e.g. deliverRemoteDB_D) removed
* Only the most high-level API is exposed to Activity handler code, making
handler code more concise and clear
Also added in this patch:
* Foundation for per-actor key support
* 1 key per actor allowed in DB
* Disabled C2S and S2S handlers now un-exported for clarity
* Audience and capability parsing automatically done for all C2S handlers
* Audience and activity composition automatically done for Vervis.Client
builder functions
Caveats:
* Actor documents still don't link to their per-actor keys; that should be the
last piece to complete per-actor key support
* No moderation and anti-spam tools yet
* Delivery API doesn't yet have good integration of persistence layer, e.g.
activity is separately encoded into bytestring for DB and for HTTP; this will
be improved in the next iteration
* Periodic delivery now done in 3 separate steps, running sequentially; it
simplifies the code, but may be changed for efficiency/robustness in the next
iterations
* Periodic delivery collects per-actor keys in a
1-DB-transaction-for-each-delivery fashion, rather than grabbing them in the
big Esqueleto query (or keeping the signed output in the DB; this isn't done
currently to allow for smooth actor key renewal)
* No support yet in the API for delivery where the actor key has already been
fetched, rather than doing a DB transaction to grab it; such support would be
just an optimization, so it's low-priority, but will be added in later
iterations
2022-10-12 16:50:11 +00:00
|
|
|
|
2023-04-29 10:40:44 +00:00
|
|
|
parseStampRoute
|
|
|
|
:: Route App -> Maybe (LocalActorBy KeyHashid, KeyHashid SigKey)
|
Improve the AP async HTTP delivery API and per-actor key support
New iteration of the ActivityPub delivery implementation and interface.
Advantages over previous interface:
* When sending a ByteString body, the sender is explicitly passed as a
parameter instead of JSON-parsing it out of the ByteString
* Clear 3 operations provided: Send, Resend and Forward
* Support for per-actor keys
* Actor-type-specific functions (e.g. deliverRemoteDB_D) removed
* Only the most high-level API is exposed to Activity handler code, making
handler code more concise and clear
Also added in this patch:
* Foundation for per-actor key support
* 1 key per actor allowed in DB
* Disabled C2S and S2S handlers now un-exported for clarity
* Audience and capability parsing automatically done for all C2S handlers
* Audience and activity composition automatically done for Vervis.Client
builder functions
Caveats:
* Actor documents still don't link to their per-actor keys; that should be the
last piece to complete per-actor key support
* No moderation and anti-spam tools yet
* Delivery API doesn't yet have good integration of persistence layer, e.g.
activity is separately encoded into bytestring for DB and for HTTP; this will
be improved in the next iteration
* Periodic delivery now done in 3 separate steps, running sequentially; it
simplifies the code, but may be changed for efficiency/robustness in the next
iterations
* Periodic delivery collects per-actor keys in a
1-DB-transaction-for-each-delivery fashion, rather than grabbing them in the
big Esqueleto query (or keeping the signed output in the DB; this isn't done
currently to allow for smooth actor key renewal)
* No support yet in the API for delivery where the actor key has already been
fetched, rather than doing a DB transaction to grab it; such support would be
just an optimization, so it's low-priority, but will be added in later
iterations
2022-10-12 16:50:11 +00:00
|
|
|
parseStampRoute (PersonStampR p i) = Just (LocalActorPerson p, i)
|
|
|
|
parseStampRoute (GroupStampR g i) = Just (LocalActorGroup g, i)
|
|
|
|
parseStampRoute (RepoStampR r i) = Just (LocalActorRepo r, i)
|
|
|
|
parseStampRoute (DeckStampR d i) = Just (LocalActorDeck d, i)
|
|
|
|
parseStampRoute (LoomStampR l i) = Just (LocalActorLoom l, i)
|
2023-06-26 22:02:54 +03:00
|
|
|
parseStampRoute (ProjectStampR r i) = Just (LocalActorProject r, i)
|
Improve the AP async HTTP delivery API and per-actor key support
New iteration of the ActivityPub delivery implementation and interface.
Advantages over previous interface:
* When sending a ByteString body, the sender is explicitly passed as a
parameter instead of JSON-parsing it out of the ByteString
* Clear 3 operations provided: Send, Resend and Forward
* Support for per-actor keys
* Actor-type-specific functions (e.g. deliverRemoteDB_D) removed
* Only the most high-level API is exposed to Activity handler code, making
handler code more concise and clear
Also added in this patch:
* Foundation for per-actor key support
* 1 key per actor allowed in DB
* Disabled C2S and S2S handlers now un-exported for clarity
* Audience and capability parsing automatically done for all C2S handlers
* Audience and activity composition automatically done for Vervis.Client
builder functions
Caveats:
* Actor documents still don't link to their per-actor keys; that should be the
last piece to complete per-actor key support
* No moderation and anti-spam tools yet
* Delivery API doesn't yet have good integration of persistence layer, e.g.
activity is separately encoded into bytestring for DB and for HTTP; this will
be improved in the next iteration
* Periodic delivery now done in 3 separate steps, running sequentially; it
simplifies the code, but may be changed for efficiency/robustness in the next
iterations
* Periodic delivery collects per-actor keys in a
1-DB-transaction-for-each-delivery fashion, rather than grabbing them in the
big Esqueleto query (or keeping the signed output in the DB; this isn't done
currently to allow for smooth actor key renewal)
* No support yet in the API for delivery where the actor key has already been
fetched, rather than doing a DB transaction to grab it; such support would be
just an optimization, so it's low-priority, but will be added in later
iterations
2022-10-12 16:50:11 +00:00
|
|
|
parseStampRoute _ = Nothing
|
2022-10-16 11:26:24 +00:00
|
|
|
|
2023-04-29 10:40:44 +00:00
|
|
|
localActorID :: LocalActorBy Entity -> ActorId
|
2022-10-16 11:26:24 +00:00
|
|
|
localActorID (LocalActorPerson (Entity _ p)) = personActor p
|
|
|
|
localActorID (LocalActorGroup (Entity _ g)) = groupActor g
|
|
|
|
localActorID (LocalActorRepo (Entity _ r)) = repoActor r
|
|
|
|
localActorID (LocalActorDeck (Entity _ d)) = deckActor d
|
|
|
|
localActorID (LocalActorLoom (Entity _ l)) = loomActor l
|
2023-06-26 22:02:54 +03:00
|
|
|
localActorID (LocalActorProject (Entity _ r)) = projectActor r
|
2022-10-25 04:54:56 +00:00
|
|
|
|
2023-04-29 10:40:44 +00:00
|
|
|
parseFedURIOld
|
|
|
|
:: ( MonadSite m
|
|
|
|
, SiteEnv m ~ site
|
|
|
|
, YesodActivityPub site
|
|
|
|
, SiteFedURIMode site ~ URIMode
|
|
|
|
)
|
|
|
|
=> FedURI
|
|
|
|
-> ExceptT Text m (Either (Route App) FedURI)
|
|
|
|
parseFedURIOld u@(ObjURI h lu) = do
|
|
|
|
hl <- hostIsLocalOld h
|
2022-10-25 04:54:56 +00:00
|
|
|
if hl
|
|
|
|
then Left <$> parseLocalURI lu
|
|
|
|
else pure $ Right u
|
|
|
|
|
|
|
|
parseLocalActorE :: Route App -> ExceptT Text Handler (LocalActorBy Key)
|
|
|
|
parseLocalActorE route = do
|
|
|
|
actorByHash <- fromMaybeE (parseLocalActor route) "Not an actor route"
|
|
|
|
unhashLocalActorE actorByHash "Invalid actor keyhashid"
|
2023-11-03 10:56:25 +02:00
|
|
|
|
|
|
|
parseLocalActorE' :: Route App -> VA.ActE (LocalActorBy Key)
|
|
|
|
parseLocalActorE' route = do
|
|
|
|
actorByHash <- fromMaybeE (parseLocalActor route) "Not an actor route"
|
|
|
|
VA.unhashLocalActorE actorByHash "Invalid actor keyhashid"
|