DB: Separate tables for CollabTopicLocalAccept and CollabTopicRemoteAccept

Previously there was just CollabTopicAccept, which worked only for local topics
but pretended to apply to both, due to directly pointing to Collab, thus
possible to insert rows even if there's a CollabTopicRemote.

The new situation is a new CollabTopicLocal table to which the local topic
things point, thus keeping the local and remote data separate and difficult to
confuse.
This commit is contained in:
fr33domlover 2022-08-30 08:28:44 +00:00
parent 9e6eb9bec6
commit b0576f9bf6
11 changed files with 316 additions and 80 deletions

View file

@ -0,0 +1,4 @@
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab

View file

@ -0,0 +1,15 @@
Repo
Collab
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab
CollabTopicLocalRepo
collab CollabId
collabNew CollabTopicLocalId
repo RepoId
UniqueCollabTopicLocalRepo collab

View file

@ -0,0 +1,15 @@
Deck
Collab
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab
CollabTopicLocalDeck
collab CollabId
collabNew CollabTopicLocalId
deck DeckId
UniqueCollabTopicLocalDeck collab

View file

@ -0,0 +1,15 @@
Loom
Collab
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab
CollabTopicLocalLoom
collab CollabId
collabNew CollabTopicLocalId
loom LoomId
UniqueCollabTopicLocalLoom collab

View file

@ -0,0 +1,15 @@
OutboxItem
Collab
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab
CollabTopicLocalAccept
collab CollabId
collabNew CollabTopicLocalId
accept OutboxItemId
UniqueCollabTopicLocalAcceptAccept accept

View file

@ -0,0 +1,6 @@
CollabTopicRemoteAccept
collab CollabTopicRemoteId
accept RemoteActivityId
UniqueCollabTopicRemoteAcceptCollab collab
UniqueCollabTopicRemoteAcceptAccept accept

View file

@ -219,14 +219,19 @@ acceptC (Entity pidUser personUser) senderActor summary audience accept = do
_ -> throwE "Accepting a Collab whose recipient is someone else"
-- Verify the Collab isn't already validated
maybeValid <- lift $ getBy $ UniqueCollabTopicAcceptCollab collabID
verifyNothingE maybeValid "Collab already Accepted by the topic"
topicActor <- lift $ getCollabTopic collabID
case topicActor of
Left (localID, _) -> do
maybeValid <- lift $ getBy $ UniqueCollabTopicLocalAcceptCollab localID
verifyNothingE maybeValid "Collab already Accepted by the local topic"
Right (remoteID, _) -> do
maybeValid <- lift $ getBy $ UniqueCollabTopicRemoteAcceptCollab remoteID
verifyNothingE maybeValid "Collab already Accepted by the remote topic"
-- Verify that Grant sender and resource are addressed by the Accept
topicActor <- lift $ getCollabTopic collabID
bitraverse_
(verifyResourceAddressed localRecips)
(verifyRemoteAddressed remoteRecips)
(verifyResourceAddressed localRecips . snd)
(verifyRemoteAddressed remoteRecips . snd)
topicActor
bitraverse_
(verifySenderAddressed localRecips)
@ -246,7 +251,7 @@ acceptC (Entity pidUser personUser) senderActor summary audience accept = do
-- Deliver the Accept activity to local recipients, and schedule
-- delivery for unavailable remote recipients
remoteRecipsHttpAccept <- do
topicHash <- bitraverse hashGrantResource pure topicActor
topicHash <- bitraverse (hashGrantResource . snd) (pure . snd) topicActor
let sieveActors = catMaybes
[ case topicHash of
Left (GrantResourceRepo r) -> Just $ LocalActorRepo r
@ -278,10 +283,10 @@ acceptC (Entity pidUser personUser) senderActor summary audience accept = do
-- If resource is local, verify it has received the Accept
topicActorLocal <-
case topicActor of
Left resource ->
Just <$> getGrantResource resource "getGrantResource"
Left (localID, resource) ->
Just . (localID,) <$> getGrantResource resource "getGrantResource"
Right _ -> pure Nothing
for_ topicActorLocal $ \ resource -> do
for_ topicActorLocal $ \ (_, resource) -> do
let resourceActorID = grantResourceActor resource
verifyActorHasItem resourceActorID acceptID "Local topic didn't receive the Accept"
@ -297,13 +302,13 @@ acceptC (Entity pidUser personUser) senderActor summary audience accept = do
-- If resource is local, approve the Collab and deliver an Accept
-- We'll refer to the resource's Accept as the "Enable" activity
deliverHttpEnable <- for topicActorLocal $ \ resource -> do
deliverHttpEnable <- for topicActorLocal $ \ (topicLocalID, resource) -> do
-- Approve the Collab in the DB
resourceOutbox <-
lift $ actorOutbox <$> getJust (grantResourceActor resource)
enableID <- lift $ insertEmptyOutboxItem resourceOutbox now
lift $ insert_ $ CollabTopicAccept collabID enableID
lift $ insert_ $ CollabTopicLocalAccept topicLocalID enableID
-- Insert the Enable to resource's outbox
(docEnable, localRecipsEnable, remoteRecipsEnable, fwdHostsEnable) <-
@ -354,22 +359,28 @@ acceptC (Entity pidUser personUser) senderActor summary audience accept = do
getCollabTopic collabID = do
maybeLocal <- do
maybeRepo <- getValBy $ UniqueCollabTopicLocalRepo collabID
maybeDeck <- getValBy $ UniqueCollabTopicLocalDeck collabID
maybeLoom <- getValBy $ UniqueCollabTopicLocalLoom collabID
return $
case (maybeRepo, maybeDeck, maybeLoom) of
(Nothing, Nothing, Nothing) -> Nothing
(Just r, Nothing, Nothing) ->
Just $ GrantResourceRepo $ collabTopicLocalRepoRepo r
(Nothing, Just d, Nothing) ->
Just $ GrantResourceDeck $ collabTopicLocalDeckDeck d
(Nothing, Nothing, Just l) ->
Just $ GrantResourceLoom $ collabTopicLocalLoomLoom l
_ -> error "Found Collab with multiple local topics"
maybeLocalID <- getKeyBy $ UniqueCollabTopicLocal collabID
for maybeLocalID $ \ localID -> do
resourceID <- do
maybeRepo <- getValBy $ UniqueCollabTopicLocalRepo localID
maybeDeck <- getValBy $ UniqueCollabTopicLocalDeck localID
maybeLoom <- getValBy $ UniqueCollabTopicLocalLoom localID
return $
case (maybeRepo, maybeDeck, maybeLoom) of
(Nothing, Nothing, Nothing) -> error "Found Collab with no specific local topic"
(Just r, Nothing, Nothing) ->
GrantResourceRepo $ collabTopicLocalRepoRepo r
(Nothing, Just d, Nothing) ->
GrantResourceDeck $ collabTopicLocalDeckDeck d
(Nothing, Nothing, Just l) ->
GrantResourceLoom $ collabTopicLocalLoomLoom l
_ -> error "Found Collab with multiple local topics"
return (localID, resourceID)
maybeRemote <- do
mr <- getValBy $ UniqueCollabTopicRemote collabID
traverse (getRemoteActorURI . collabTopicRemoteActor) mr
mr <- getBy $ UniqueCollabTopicRemote collabID
for mr $ \ (Entity remoteID remote) -> do
u <- getRemoteActorURI $ collabTopicRemoteActor remote
return (remoteID, u)
requireEitherM
maybeLocal
maybeRemote
@ -1518,8 +1529,9 @@ createTicketTrackerC (Entity pidUser personUser) senderActor summary audience tr
insertCollab did obiidGrant = do
cid <- insert Collab
insert_ $ CollabTopicLocalDeck cid did
insert_ $ CollabTopicAccept cid obiidGrant
ctlid <- insert $ CollabTopicLocal cid
insert_ $ CollabTopicLocalDeck ctlid did
insert_ $ CollabTopicLocalAccept ctlid obiidGrant
insert_ $ CollabSenderLocal cid obiidGrant
insert_ $ CollabRecipLocal cid pidUser
insert_ $ CollabFulfillsLocalTopicCreation cid
@ -2082,14 +2094,15 @@ grantC (Entity pidUser personUser) senderActor muCap summary audience grant = do
insertCollab resource recipient grantID = do
collabID <- insert Collab
case resource of
Left local ->
Left local -> do
topicID <- insert $ CollabTopicLocal collabID
case local of
GrantResourceRepo (Entity repoID _) ->
insert_ $ CollabTopicLocalRepo collabID repoID
insert_ $ CollabTopicLocalRepo topicID repoID
GrantResourceDeck (Entity deckID _) ->
insert_ $ CollabTopicLocalDeck collabID deckID
insert_ $ CollabTopicLocalDeck topicID deckID
GrantResourceLoom (Entity loomID _) ->
insert_ $ CollabTopicLocalLoom collabID loomID
insert_ $ CollabTopicLocalLoom topicID loomID
Right (remoteID, actorID, _) ->
insert_ $ CollabTopicRemote collabID remoteID actorID Nothing
insert_ $ CollabSenderLocal collabID grantID

View file

@ -167,14 +167,15 @@ checkRepoAccess' mpid op repoID = do
where
asCollab rid pid = do
fmap (const Developer) . listToMaybe <$> do
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalRepoCollab E.==. accept E.^. CollabTopicAcceptCollab
E.on $ topic E.^. CollabTopicLocalRepoCollab E.==. recip E.^. CollabRecipLocalCollab
E.select $ E.from $ \ (repo `E.InnerJoin` topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalId E.==. accept E.^. CollabTopicLocalAcceptCollab
E.on $ topic E.^. CollabTopicLocalCollab E.==. recip E.^. CollabRecipLocalCollab
E.on $ repo E.^. CollabTopicLocalRepoCollab E.==. topic E.^. CollabTopicLocalId
E.where_ $
topic E.^. CollabTopicLocalRepoRepo E.==. E.val rid E.&&.
repo E.^. CollabTopicLocalRepoRepo E.==. E.val rid E.&&.
recip E.^. CollabRecipLocalPerson E.==. E.val pid
E.limit 1
return $ topic E.^. CollabTopicLocalRepoCollab
return $ topic E.^. CollabTopicLocalCollab
asUser = fmap RoleID . repoCollabUser
asAnon = fmap RoleID . repoCollabAnon
@ -201,14 +202,15 @@ checkRepoAccess mpid op repoHash = do
where
asCollab rid pid = do
fmap (const Developer) . listToMaybe <$> do
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalRepoCollab E.==. accept E.^. CollabTopicAcceptCollab
E.on $ topic E.^. CollabTopicLocalRepoCollab E.==. recip E.^. CollabRecipLocalCollab
E.select $ E.from $ \ (repo `E.InnerJoin` topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalId E.==. accept E.^. CollabTopicLocalAcceptCollab
E.on $ topic E.^. CollabTopicLocalCollab E.==. recip E.^. CollabRecipLocalCollab
E.on $ repo E.^. CollabTopicLocalRepoCollab E.==. topic E.^. CollabTopicLocalId
E.where_ $
topic E.^. CollabTopicLocalRepoRepo E.==. E.val rid E.&&.
repo E.^. CollabTopicLocalRepoRepo E.==. E.val rid E.&&.
recip E.^. CollabRecipLocalPerson E.==. E.val pid
E.limit 1
return $ topic E.^. CollabTopicLocalRepoCollab
return $ topic E.^. CollabTopicLocalCollab
asUser = fmap RoleID . repoCollabUser
asAnon = fmap RoleID . repoCollabAnon
@ -236,14 +238,15 @@ checkProjectAccess mpid op deckHash = do
where
asCollab jid pid = do
fmap (const Developer) . listToMaybe <$> do
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalDeckCollab E.==. accept E.^. CollabTopicAcceptCollab
E.on $ topic E.^. CollabTopicLocalDeckCollab E.==. recip E.^. CollabRecipLocalCollab
E.select $ E.from $ \ (deck `E.InnerJoin` topic `E.InnerJoin` recip `E.InnerJoin` accept) -> do
E.on $ topic E.^. CollabTopicLocalId E.==. accept E.^. CollabTopicLocalAcceptCollab
E.on $ topic E.^. CollabTopicLocalCollab E.==. recip E.^. CollabRecipLocalCollab
E.on $ deck E.^. CollabTopicLocalDeckCollab E.==. topic E.^. CollabTopicLocalId
E.where_ $
topic E.^. CollabTopicLocalDeckDeck E.==. E.val jid E.&&.
deck E.^. CollabTopicLocalDeckDeck E.==. E.val jid E.&&.
recip E.^. CollabRecipLocalPerson E.==. E.val pid
E.limit 1
return $ topic E.^. CollabTopicLocalDeckCollab
return $ topic E.^. CollabTopicLocalCollab
asUser = fmap RoleID . deckCollabUser
asAnon = fmap RoleID . deckCollabAnon
@ -325,26 +328,32 @@ verifyCapability capability personID resource = do
verifyNothingE maybeRemote "Collab is for some other, remote topic"
-- Find the local topic, on which this Collab gives access
topic <- lift $ do
maybeRepo <- getValBy $ UniqueCollabTopicLocalRepo cid
maybeDeck <- getValBy $ UniqueCollabTopicLocalDeck cid
maybeLoom <- getValBy $ UniqueCollabTopicLocalLoom cid
case (maybeRepo, maybeDeck, maybeLoom) of
(Nothing, Nothing, Nothing) -> error "Collab without topic"
(Just r, Nothing, Nothing) ->
return $ GrantResourceRepo $ collabTopicLocalRepoRepo r
(Nothing, Just d, Nothing) ->
return $ GrantResourceDeck $ collabTopicLocalDeckDeck d
(Nothing, Nothing, Just l) ->
return $ GrantResourceLoom $ collabTopicLocalLoomLoom l
_ -> error "Collab with multiple topics"
(topic, topicLocalID) <- lift $ do
localID <- do
maybeLocal <- getKeyBy $ UniqueCollabTopicLocal cid
case maybeLocal of
Nothing -> error "Collab without topic"
Just l -> return l
maybeRepo <- getValBy $ UniqueCollabTopicLocalRepo localID
maybeDeck <- getValBy $ UniqueCollabTopicLocalDeck localID
maybeLoom <- getValBy $ UniqueCollabTopicLocalLoom localID
(,localID) <$>
case (maybeRepo, maybeDeck, maybeLoom) of
(Nothing, Nothing, Nothing) -> error "Collab without local topic"
(Just r, Nothing, Nothing) ->
return $ GrantResourceRepo $ collabTopicLocalRepoRepo r
(Nothing, Just d, Nothing) ->
return $ GrantResourceDeck $ collabTopicLocalDeckDeck d
(Nothing, Nothing, Just l) ->
return $ GrantResourceLoom $ collabTopicLocalLoomLoom l
_ -> error "Collab with multiple topics"
-- Verify the topic matches the resource specified
unless (topic == resource) $
throwE "Capability topic is some other local resource"
-- Verify that the resource has accepted the grant, making it valid
maybeAccept <- lift $ getBy $ UniqueCollabTopicAcceptCollab cid
maybeAccept <- lift $ getBy $ UniqueCollabTopicLocalAcceptCollab topicLocalID
_ <- fromMaybeE maybeAccept "Collab not approved by the resource"
-- Since there are currently no roles, and grants allow only the "Admin"
@ -387,27 +396,23 @@ verifyCapabilityRemote capability personID resourceID = do
throwE "Collab recipient is some other Person"
-- Verify the topic isn't local
maybeRepo <- lift $ fmap (const ()) <$> getValBy (UniqueCollabTopicLocalRepo cid)
maybeDeck <- lift $ fmap (const ()) <$> getValBy (UniqueCollabTopicLocalDeck cid)
maybeLoom <- lift $ fmap (const ()) <$> getValBy (UniqueCollabTopicLocalLoom cid)
case length $ catMaybes [maybeRepo, maybeDeck, maybeLoom] of
0 -> return ()
1 -> throwE "Collab is for some other, local topic"
_ -> error "Collab with multiple topics"
maybeLocalTopic <- lift $ getBy $ UniqueCollabTopicLocal cid
verifyNothingE maybeLocalTopic "Collab is for some other, local topic"
-- Find the remote topic, on which this Collab gives access
topicID <- do
maybeRemote <- lift $ getValBy $ UniqueCollabTopicRemote cid
(topicRemoteID, topicObjectID) <- do
maybeRemote <- lift $ getBy $ UniqueCollabTopicRemote cid
case maybeRemote of
Nothing -> error "Collab without topic"
Just remote -> return $ collabTopicRemoteTopic remote
Just (Entity remoteID remote) ->
return (remoteID, collabTopicRemoteTopic remote)
-- Verify the topic matches the resource specified
unless (topicID == resourceID) $
unless (topicObjectID == resourceID) $
throwE "Capability topic is some other remote resource"
-- Verify that the resource has accepted the grant, making it valid
maybeAccept <- lift $ getBy $ UniqueCollabTopicAcceptCollab cid
maybeAccept <- lift $ getBy $ UniqueCollabTopicRemoteAcceptCollab topicRemoteID
_ <- fromMaybeE maybeAccept "Collab not approved by the resource"
-- Since there are currently no roles, and grants allow only the "Admin"

View file

@ -110,6 +110,12 @@ removeUnique' entity@(EntityName e) unique =
removeUnique entity $
fromString $ "Unique" ++ T.unpack e ++ T.unpack unique
renameUnique' entity@(EntityName e) old new =
renameUnique
entity
(fromString $ "Unique" ++ T.unpack e ++ T.unpack old)
(fromString $ "Unique" ++ T.unpack e ++ T.unpack new)
changes :: (MonadSite m, SiteEnv m ~ App) => Host -> HashidsContext -> [Mig m]
changes hLocal ctx =
[ -- 1
@ -2434,6 +2440,118 @@ changes hLocal ctx =
insert_ $ CollabTopicAccept426 collabID itemID
-- 427
, addFieldRefRequiredEmpty "CollabTopicRemote" "actor" "RemoteActor"
-- 428
, addEntities model_428_collab_topic_local
-- 429
, addFieldRefRequired''
"CollabTopicLocalRepo"
(do collabID <- insert Collab429
insertEntity $ CollabTopicLocal429 collabID
)
(Just $ \ (Entity topicTemp (CollabTopicLocal429 collabTemp)) -> do
collabs <- selectList [] []
for_ collabs $ \ (Entity topicID topic) -> do
localID <-
insert $ CollabTopicLocal429 $ collabTopicLocalRepo429Collab topic
update topicID [CollabTopicLocalRepo429CollabNew =. localID]
delete topicTemp
delete collabTemp
)
"collabNew"
"CollabTopicLocal"
-- 430
, addFieldRefRequired''
"CollabTopicLocalDeck"
(do collabID <- insert Collab430
insertEntity $ CollabTopicLocal430 collabID
)
(Just $ \ (Entity topicTemp (CollabTopicLocal430 collabTemp)) -> do
collabs <- selectList [] []
for_ collabs $ \ (Entity topicID topic) -> do
localID <-
insert $ CollabTopicLocal430 $ collabTopicLocalDeck430Collab topic
update topicID [CollabTopicLocalDeck430CollabNew =. localID]
delete topicTemp
delete collabTemp
)
"collabNew"
"CollabTopicLocal"
-- 431
, addFieldRefRequired''
"CollabTopicLocalLoom"
(do collabID <- insert Collab431
insertEntity $ CollabTopicLocal431 collabID
)
(Just $ \ (Entity topicTemp (CollabTopicLocal431 collabTemp)) -> do
collabs <- selectList [] []
for_ collabs $ \ (Entity topicID topic) -> do
localID <-
insert $ CollabTopicLocal431 $ collabTopicLocalLoom431Collab topic
update topicID [CollabTopicLocalLoom431CollabNew =. localID]
delete topicTemp
delete collabTemp
)
"collabNew"
"CollabTopicLocal"
-- 432
, removeUnique' "CollabTopicLocalRepo" ""
-- 433
, addUnique' "CollabTopicLocalRepo" "" ["collabNew"]
-- 434
, removeUnique' "CollabTopicLocalDeck" ""
-- 435
, addUnique' "CollabTopicLocalDeck" "" ["collabNew"]
-- 436
, removeUnique' "CollabTopicLocalLoom" ""
-- 437
, addUnique' "CollabTopicLocalLoom" "" ["collabNew"]
-- 438
, removeField "CollabTopicLocalRepo" "collab"
-- 439
, renameField "CollabTopicLocalRepo" "collabNew" "collab"
-- 440
, removeField "CollabTopicLocalDeck" "collab"
-- 441
, renameField "CollabTopicLocalDeck" "collabNew" "collab"
-- 442
, removeField "CollabTopicLocalLoom" "collab"
-- 443
, renameField "CollabTopicLocalLoom" "collabNew" "collab"
-- 444
, removeUnique' "CollabTopicAccept" "Collab"
-- 445
, renameUnique "CollabTopicAccept" "UniqueCollabTopicAcceptAccept" "UniqueCollabTopicLocalAcceptAccept"
-- 446
, renameEntity "CollabTopicAccept" "CollabTopicLocalAccept"
-- 447
, addFieldRefRequired''
"CollabTopicLocalAccept"
(do collabID <- insert Collab447
insertEntity $ CollabTopicLocal447 collabID
)
(Just $ \ (Entity topicTemp (CollabTopicLocal447 collabTemp)) -> do
collabs <- selectList [] []
for_ collabs $ \ (Entity topicID topic) -> do
localID <-
insert $ CollabTopicLocal447 $ collabTopicLocalAccept447Collab topic
update topicID [CollabTopicLocalAccept447CollabNew =. localID]
delete topicTemp
delete collabTemp
)
"collabNew"
"CollabTopicLocal"
-- 448
, addUnique' "CollabTopicLocalAccept" "Collab" ["collabNew"]
-- 449
, removeField "CollabTopicLocalAccept" "collab"
-- 450
, renameField "CollabTopicLocalAccept" "collabNew" "collab"
-- 451
, addEntities model_451_collab_remote_accept
]
migrateDB

View file

@ -629,3 +629,21 @@ model_425_collab_accept = $(schema "425_2022-08-21_collab_accept")
makeEntitiesMigration "426"
$(modelFile "migrations/426_2022-08-21_collab_accept_mig.model")
model_428_collab_topic_local :: [Entity SqlBackend]
model_428_collab_topic_local = $(schema "428_2022-08-29_collab_topic_local")
makeEntitiesMigration "429"
$(modelFile "migrations/429_2022-08-30_collab_repo.model")
makeEntitiesMigration "430"
$(modelFile "migrations/430_2022-08-30_collab_deck.model")
makeEntitiesMigration "431"
$(modelFile "migrations/431_2022-08-30_collab_loom.model")
makeEntitiesMigration "447"
$(modelFile "migrations/447_2022-08-30_collab_accept.model")
model_451_collab_remote_accept :: [Entity SqlBackend]
model_451_collab_remote_accept = $(schema "451_2022-08-30_collab_remote_accept")

View file

@ -595,30 +595,35 @@ Collab
--
-- UniqueCollabRoleLocal collab
CollabTopicLocalRepo
CollabTopicLocal
collab CollabId
UniqueCollabTopicLocal collab
CollabTopicLocalRepo
collab CollabTopicLocalId
repo RepoId
UniqueCollabTopicLocalRepo collab
CollabTopicLocalDeck
collab CollabId
collab CollabTopicLocalId
deck DeckId
UniqueCollabTopicLocalDeck collab
CollabTopicLocalLoom
collab CollabId
collab CollabTopicLocalId
loom LoomId
UniqueCollabTopicLocalLoom collab
CollabTopicAccept
collab CollabId
CollabTopicLocalAccept
collab CollabTopicLocalId
accept OutboxItemId
UniqueCollabTopicAcceptCollab collab
UniqueCollabTopicAcceptAccept accept
UniqueCollabTopicLocalAcceptCollab collab
UniqueCollabTopicLocalAcceptAccept accept
CollabTopicRemote
collab CollabId
@ -628,6 +633,13 @@ CollabTopicRemote
UniqueCollabTopicRemote collab
CollabTopicRemoteAccept
collab CollabTopicRemoteId
accept RemoteActivityId
UniqueCollabTopicRemoteAcceptCollab collab
UniqueCollabTopicRemoteAcceptAccept accept
-------------------------------- Collab sender -------------------------------
CollabSenderLocal