S2S: Implement project handlers, based on the Deck ones
This commit is contained in:
parent
224c290b04
commit
232a0cd4df
1 changed files with 235 additions and 0 deletions
|
@ -70,6 +70,29 @@ import Vervis.Persist.Collab
|
||||||
import Vervis.Persist.Discussion
|
import Vervis.Persist.Discussion
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
|
||||||
|
-- Meaning: An actor accepted something
|
||||||
|
-- Behavior:
|
||||||
|
-- * If it's on an Invite where I'm the resource:
|
||||||
|
-- * Verify the Accept is by the Invite target
|
||||||
|
-- * Forward the Accept to my followers
|
||||||
|
-- * Send a Grant:
|
||||||
|
-- * To: Accepter (i.e. Invite target)
|
||||||
|
-- * CC: Invite sender, Accepter's followers, my followers
|
||||||
|
-- * If it's on a Join where I'm the resource:
|
||||||
|
-- * Verify the Accept is authorized
|
||||||
|
-- * Forward the Accept to my followers
|
||||||
|
-- * Send a Grant:
|
||||||
|
-- * To: Join sender
|
||||||
|
-- * CC: Accept sender, Join sender's followers, my followers
|
||||||
|
-- * Otherwise respond with error
|
||||||
|
projectAccept
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Accept URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectAccept = topicAccept projectActor GrantResourceProject
|
||||||
|
|
||||||
-- Meaning: Someone has created a project with my ID URI
|
-- Meaning: Someone has created a project with my ID URI
|
||||||
-- Behavior:
|
-- Behavior:
|
||||||
-- * Verify I'm in a just-been-created state
|
-- * Verify I'm in a just-been-created state
|
||||||
|
@ -135,11 +158,223 @@ projectFollow now recipProjectID verse follow = do
|
||||||
(\ _ -> pure [])
|
(\ _ -> pure [])
|
||||||
now recipProjectID verse follow
|
now recipProjectID verse follow
|
||||||
|
|
||||||
|
-- Meaning: An actor A invited actor B to a resource
|
||||||
|
-- Behavior:
|
||||||
|
-- * Verify the resource is me
|
||||||
|
-- * Verify A isn't inviting themselves
|
||||||
|
-- * Verify A is authorized by me to invite actors to me
|
||||||
|
-- * Verify B doesn't already have an invite/join/grant for me
|
||||||
|
-- * Remember the invite in DB
|
||||||
|
-- * Forward the Invite to my followers
|
||||||
|
projectInvite
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Invite URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectInvite =
|
||||||
|
topicInvite
|
||||||
|
projectActor GrantResourceProject
|
||||||
|
CollabTopicProjectProject CollabTopicProjectCollab CollabTopicProject
|
||||||
|
|
||||||
|
-- Meaning: An actor A asked to join a resource
|
||||||
|
-- Behavior:
|
||||||
|
-- * Verify the resource is me
|
||||||
|
-- * Verify A doesn't already have an invite/join/grant for me
|
||||||
|
-- * Remember the join in DB
|
||||||
|
-- * Forward the Join to my followers
|
||||||
|
projectJoin
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Join URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectJoin =
|
||||||
|
topicJoin
|
||||||
|
projectActor GrantResourceProject
|
||||||
|
CollabTopicProjectProject CollabTopicProjectCollab CollabTopicProject
|
||||||
|
|
||||||
|
-- Meaning: An actor rejected something
|
||||||
|
-- Behavior:
|
||||||
|
-- * If it's on an Invite where I'm the resource:
|
||||||
|
-- * Verify the Reject is by the Invite target
|
||||||
|
-- * Remove the relevant Collab record from DB
|
||||||
|
-- * Forward the Reject to my followers
|
||||||
|
-- * Send a Reject on the Invite:
|
||||||
|
-- * To: Rejecter (i.e. Invite target)
|
||||||
|
-- * CC: Invite sender, Rejecter's followers, my followers
|
||||||
|
-- * If it's on a Join where I'm the resource:
|
||||||
|
-- * Verify the Reject is authorized
|
||||||
|
-- * Remove the relevant Collab record from DB
|
||||||
|
-- * Forward the Reject to my followers
|
||||||
|
-- * Send a Reject:
|
||||||
|
-- * To: Join sender
|
||||||
|
-- * CC: Reject sender, Join sender's followers, my followers
|
||||||
|
-- * Otherwise respond with error
|
||||||
|
projectReject
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Reject URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectReject = topicReject projectActor GrantResourceProject
|
||||||
|
|
||||||
|
-- Meaning: An actor A is removing actor B from a resource
|
||||||
|
-- Behavior:
|
||||||
|
-- * Verify the resource is me
|
||||||
|
-- * Verify A isn't removing themselves
|
||||||
|
-- * Verify A is authorized by me to remove actors from me
|
||||||
|
-- * Verify B already has a Grant for me
|
||||||
|
-- * Remove the whole Collab record from DB
|
||||||
|
-- * Forward the Remove to my followers
|
||||||
|
-- * Send a Revoke:
|
||||||
|
-- * To: Actor B
|
||||||
|
-- * CC: Actor A, B's followers, my followers
|
||||||
|
projectRemove
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Remove URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectRemove =
|
||||||
|
topicRemove
|
||||||
|
projectActor GrantResourceProject
|
||||||
|
CollabTopicProjectProject CollabTopicProjectCollab
|
||||||
|
|
||||||
|
-- Meaning: An actor is undoing some previous action
|
||||||
|
-- Behavior:
|
||||||
|
-- * If they're undoing their Following of me:
|
||||||
|
-- * Record it in my DB
|
||||||
|
-- * Publish and send an Accept only to the sender
|
||||||
|
-- * Otherwise respond with an error
|
||||||
|
projectUndo
|
||||||
|
:: UTCTime
|
||||||
|
-> ProjectId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Undo URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
projectUndo now recipProjectID (Verse authorIdMsig body) (AP.Undo uObject) = do
|
||||||
|
|
||||||
|
-- Check input
|
||||||
|
undone <-
|
||||||
|
first (\ (actor, _, item) -> (actor, item)) <$>
|
||||||
|
parseActivityURI' uObject
|
||||||
|
|
||||||
|
-- Verify the capability URI, if provided, is one of:
|
||||||
|
-- * Outbox item URI of a local actor, i.e. a local activity
|
||||||
|
-- * A remote URI
|
||||||
|
maybeCapability <-
|
||||||
|
for (AP.activityCapability $ actbActivity body) $ \ uCap ->
|
||||||
|
nameExceptT "Undo capability" $
|
||||||
|
first (\ (actor, _, item) -> (actor, item)) <$>
|
||||||
|
parseActivityURI' uCap
|
||||||
|
|
||||||
|
maybeNew <- withDBExcept $ do
|
||||||
|
|
||||||
|
-- Grab me from DB
|
||||||
|
(projectRecip, actorRecip) <- lift $ do
|
||||||
|
p <- getJust recipProjectID
|
||||||
|
(p,) <$> getJust (projectActor p)
|
||||||
|
|
||||||
|
-- Insert the Undo to my inbox
|
||||||
|
mractid <- lift $ insertToInbox now authorIdMsig body (actorInbox actorRecip) False
|
||||||
|
for mractid $ \ _undoDB -> do
|
||||||
|
|
||||||
|
maybeUndo <- runMaybeT $ do
|
||||||
|
|
||||||
|
-- Find the undone activity in our DB
|
||||||
|
undoneDB <- MaybeT $ getActivity undone
|
||||||
|
|
||||||
|
let followers = actorFollowers actorRecip
|
||||||
|
asum
|
||||||
|
[ tryUnfollow followers undoneDB authorIdMsig
|
||||||
|
]
|
||||||
|
|
||||||
|
(sieve, audience) <-
|
||||||
|
fromMaybeE
|
||||||
|
maybeUndo
|
||||||
|
"Undone activity isn't a Follow related to me"
|
||||||
|
|
||||||
|
-- Prepare an Accept activity and insert to project's outbox
|
||||||
|
acceptID <- lift $ insertEmptyOutboxItem' (actorOutbox actorRecip) now
|
||||||
|
accept@(actionAccept, _, _, _) <- lift $ lift $ prepareAccept audience
|
||||||
|
_luAccept <- lift $ updateOutboxItem' (LocalActorProject recipProjectID) acceptID actionAccept
|
||||||
|
|
||||||
|
return (projectActor projectRecip, sieve, acceptID, accept)
|
||||||
|
|
||||||
|
case maybeNew of
|
||||||
|
Nothing -> done "I already have this activity in my inbox"
|
||||||
|
Just (actorID, sieve, acceptID, (actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept)) -> do
|
||||||
|
forwardActivity
|
||||||
|
authorIdMsig body (LocalActorProject recipProjectID) actorID sieve
|
||||||
|
lift $ sendActivity
|
||||||
|
(LocalActorProject recipProjectID) actorID localRecipsAccept
|
||||||
|
remoteRecipsAccept fwdHostsAccept acceptID actionAccept
|
||||||
|
done
|
||||||
|
"Undid the Follow, forwarded the Undo and published Accept"
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
tryUnfollow projectFollowersID (Left (_actorByKey, _actorE, outboxItemID)) (Left (_, actorID, _)) = do
|
||||||
|
Entity followID follow <-
|
||||||
|
MaybeT $ lift $ getBy $ UniqueFollowFollow outboxItemID
|
||||||
|
let followerID = followActor follow
|
||||||
|
followerSetID = followTarget follow
|
||||||
|
verifyTargetMe followerSetID
|
||||||
|
unless (followerID == actorID) $
|
||||||
|
lift $ throwE "You're trying to Undo someone else's Follow"
|
||||||
|
lift $ lift $ delete followID
|
||||||
|
audSenderOnly <- lift $ lift $ lift $ makeAudSenderOnly authorIdMsig
|
||||||
|
return (makeRecipientSet [] [], [audSenderOnly])
|
||||||
|
where
|
||||||
|
verifyTargetMe followerSetID = guard $ followerSetID == projectFollowersID
|
||||||
|
tryUnfollow projectFollowersID (Right remoteActivityID) (Right (author, _, _)) = do
|
||||||
|
Entity remoteFollowID remoteFollow <-
|
||||||
|
MaybeT $ lift $ getBy $ UniqueRemoteFollowFollow remoteActivityID
|
||||||
|
let followerID = remoteFollowActor remoteFollow
|
||||||
|
followerSetID = remoteFollowTarget remoteFollow
|
||||||
|
verifyTargetMe followerSetID
|
||||||
|
unless (followerID == remoteAuthorId author) $
|
||||||
|
lift $ throwE "You're trying to Undo someone else's Follow"
|
||||||
|
lift $ lift $ delete remoteFollowID
|
||||||
|
audSenderOnly <- lift $ lift $ lift $ makeAudSenderOnly authorIdMsig
|
||||||
|
return (makeRecipientSet [] [], [audSenderOnly])
|
||||||
|
where
|
||||||
|
verifyTargetMe followerSetID = guard $ followerSetID == projectFollowersID
|
||||||
|
tryUnfollow _ _ _ = mzero
|
||||||
|
|
||||||
|
prepareAccept audience = do
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
|
||||||
|
uUndo <- getActivityURI authorIdMsig
|
||||||
|
let (recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
|
||||||
|
collectAudience audience
|
||||||
|
|
||||||
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
|
action = AP.Action
|
||||||
|
{ AP.actionCapability = Nothing
|
||||||
|
, AP.actionSummary = Nothing
|
||||||
|
, AP.actionAudience = AP.Audience recips [] [] [] [] []
|
||||||
|
, AP.actionFulfills = []
|
||||||
|
, AP.actionSpecific = AP.AcceptActivity AP.Accept
|
||||||
|
{ AP.acceptObject = uUndo
|
||||||
|
, AP.acceptResult = Nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (action, recipientSet, remoteActors, fwdHosts)
|
||||||
|
|
||||||
projectBehavior :: UTCTime -> ProjectId -> VerseExt -> ActE (Text, Act (), Next)
|
projectBehavior :: UTCTime -> ProjectId -> VerseExt -> ActE (Text, Act (), Next)
|
||||||
projectBehavior now projectID (Left verse@(Verse _authorIdMsig body)) =
|
projectBehavior now projectID (Left verse@(Verse _authorIdMsig body)) =
|
||||||
case AP.activitySpecific $ actbActivity body of
|
case AP.activitySpecific $ actbActivity body of
|
||||||
|
AP.AcceptActivity accept -> projectAccept now projectID verse accept
|
||||||
AP.CreateActivity create -> projectCreate now projectID verse create
|
AP.CreateActivity create -> projectCreate now projectID verse create
|
||||||
AP.FollowActivity follow -> projectFollow now projectID verse follow
|
AP.FollowActivity follow -> projectFollow now projectID verse follow
|
||||||
|
AP.InviteActivity invite -> projectInvite now projectID verse invite
|
||||||
|
AP.JoinActivity join -> projectJoin now projectID verse join
|
||||||
|
AP.RejectActivity reject -> projectReject now projectID verse reject
|
||||||
|
AP.RemoveActivity remove -> projectRemove now projectID verse remove
|
||||||
|
AP.UndoActivity undo -> projectUndo now projectID verse undo
|
||||||
_ -> throwE "Unsupported activity type for Project"
|
_ -> throwE "Unsupported activity type for Project"
|
||||||
projectBehavior _ _ (Right _) = throwE "ClientMsgs aren't supported for Project"
|
projectBehavior _ _ (Right _) = throwE "ClientMsgs aren't supported for Project"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue