From a03968ca0bb1cf4eaa7b29d53539f7b6888e9dd3 Mon Sep 17 00:00:00 2001 From: Pere Lev <pere@towards.vision> Date: Thu, 8 Aug 2024 13:08:18 +0300 Subject: [PATCH] HomeR, *NewR: List grants from extensions, not just direct ones --- src/Vervis/Field/Person.hs | 79 ++++++++++++--------- src/Vervis/Handler/Client.hs | 114 ++++++++++++++++++++++++++---- src/Vervis/Persist/Collab.hs | 131 +++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 46 deletions(-) diff --git a/src/Vervis/Field/Person.hs b/src/Vervis/Field/Person.hs index b54ccb0..4415da5 100644 --- a/src/Vervis/Field/Person.hs +++ b/src/Vervis/Field/Person.hs @@ -22,10 +22,12 @@ module Vervis.Field.Person where import Control.Monad.Trans.Except +import Data.Bitraversable import Data.Char (isDigit) import Data.Maybe import Data.Text (Text) -import Database.Esqueleto +import Data.Traversable +import Database.Persist import Yesod.Core import Yesod.Form.Fields import Yesod.Form.Functions @@ -43,16 +45,22 @@ import qualified Web.ActivityPub as AP import Control.Monad.Trans.Except.Local import Data.Char.Local (isAsciiLetter) +import Database.Persist.Local import Vervis.Actor import Vervis.Data.Actor +import Vervis.Data.Collab import Vervis.FedURI import Vervis.Foundation import Vervis.Model import Vervis.Model.Ident (text2shr) +import Vervis.Persist.Actor +import Vervis.Persist.Collab import Vervis.Recipient import Vervis.Settings +import qualified Vervis.Recipient as VR + checkPassLength :: Field Handler Text -> Field Handler Text checkPassLength = let msg :: Text @@ -108,54 +116,57 @@ capField = checkMMap toCap fst fedUriField runExceptT $ (u,) <$> nameExceptT "Capability URI" (parseActivityURI u) factoryField personID = selectField $ do - l <- runDB $ do - local <- - E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable `E.InnerJoin` factory `E.InnerJoin` resource `E.InnerJoin` actor) -> do - E.on $ resource E.^. ResourceActor E.==. actor E.^. ActorId - E.on $ factory E.^. FactoryResource E.==. resource E.^. ResourceId - E.on $ topic E.^. PermitTopicLocalTopic E.==. factory E.^. FactoryResource - E.on $ topic E.^. PermitTopicLocalId E.==. enable E.^. PermitTopicEnableLocalTopic - E.on $ permit E.^. PermitId E.==. topic E.^. PermitTopicLocalPermit - E.where_ $ - permit E.^. PermitPerson E.==. E.val personID E.&&. - permit E.^. PermitRole `E.in_` E.valList [AP.RoleWrite, AP.RoleMaintain, AP.RoleAdmin] - return (factory E.^. FactoryId, actor E.^. ActorName, enable E.^. PermitTopicEnableLocalGrant) - remote <- - E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable `E.InnerJoin` actor `E.InnerJoin` object `E.InnerJoin` i `E.InnerJoin` ract `E.InnerJoin` ro) -> do - E.on $ ract E.^. RemoteActivityIdent E.==. ro E.^. RemoteObjectId - E.on $ enable E.^. PermitTopicEnableRemoteGrant E.==. ract E.^. RemoteActivityId - E.on $ object E.^. RemoteObjectInstance E.==. i E.^. InstanceId - E.on $ actor E.^. RemoteActorIdent E.==. object E.^. RemoteObjectId - E.on $ topic E.^. PermitTopicRemoteActor E.==. actor E.^. RemoteActorId - E.on $ topic E.^. PermitTopicRemoteId E.==. enable E.^. PermitTopicEnableRemoteTopic - E.on $ permit E.^. PermitId E.==. topic E.^. PermitTopicRemotePermit - E.where_ $ - permit E.^. PermitPerson E.==. E.val personID E.&&. - actor E.^. RemoteActorType E.==. E.val AP.ActorTypeFactory E.&&. - permit E.^. PermitRole `E.in_` E.valList [AP.RoleWrite, AP.RoleMaintain, AP.RoleAdmin] - return (i E.^. InstanceHost, object E.^. RemoteObjectIdent, actor E.^. RemoteActorName, ro E.^. RemoteObjectIdent) - return $ map Left local ++ map Right remote + rows <- runDB $ do + resourceIDs <- map (factoryResource . entityVal) <$> selectList [] [] + actorIDs <- selectKeysList [RemoteActorType ==. AP.ActorTypeFactory] [] + permits <- getPermitsForResources personID (Just resourceIDs) (Just actorIDs) AP.RoleWrite + for permits $ \ (resource, grant, _, _) -> do + factory <- + bitraverse + (\ resourceID -> do + Resource actorID <- getJust resourceID + actor <- getJust actorID + factoryID <- getKeyByJust $ UniqueFactory resourceID + return (factoryID, actorName actor) + ) + (\ actorID -> do + actor <- getJust actorID + uActor <- getRemoteActorURI actor + return (uActor, remoteActorName actor) + ) + resource + return (factory, grant) + + hashActor <- VR.getHashLocalActor hashFactory <- getEncodeKeyHashid hashItem <- getEncodeKeyHashid encodeRouteHome <- getEncodeRouteHome + let renderGrant = \case + Left (byKey, grantID) -> + encodeRouteHome $ activityRoute (hashActor byKey) (hashItem grantID) + Right uGrant -> + uGrant optionsPairs $ - map (\case - Left (E.Value factoryID, E.Value name, E.Value grantID) -> + map (\ (factory, grant) -> + case factory of + Left (factoryID, name) -> ( T.concat [ "*", keyHashidText $ hashFactory factoryID , " ", name ] , ( encodeRouteHome $ FactoryR $ hashFactory factoryID - , encodeRouteHome $ FactoryOutboxItemR (hashFactory factoryID) (hashItem grantID) + , renderGrant grant ) ) - Right (E.Value h, E.Value lu, E.Value mname, E.Value luGrant) -> + Right (uActor@(ObjURI h lu), mname) -> ( T.concat [ renderObjURI $ ObjURI h lu , " " , fromMaybe "(?)" mname ] - , (ObjURI h lu, ObjURI h luGrant) + , ( uActor + , renderGrant grant + ) ) ) - l + rows diff --git a/src/Vervis/Handler/Client.hs b/src/Vervis/Handler/Client.hs index 1e6c79a..d15c166 100644 --- a/src/Vervis/Handler/Client.hs +++ b/src/Vervis/Handler/Client.hs @@ -73,6 +73,7 @@ import Control.Monad.Trans.Except import Data.Aeson import Data.Bifunctor import Data.Bitraversable +import Data.Foldable import Data.Function import Data.Maybe import Data.List @@ -270,10 +271,39 @@ getHomeR = do , AP.grantContext g ) + getVia extendID = do + sender <- + requireEitherAlt + (getValBy $ UniquePermitTopicExtendLocal extendID) + (getValBy $ UniquePermitTopicExtendRemote extendID) + "PermitTopicExtend* neither" + "PermitTopicExtend* both" + case sender of + Left (PermitTopicExtendLocal _ enableID grantID) -> do + PermitTopicEnableLocal _ topicID _ <- getJust enableID + byk <- getPermitTopicLocal topicID + bye <- do + m <- getLocalResourceEntity byk + case m of + Nothing -> error "I just found this PermitTopicLocal in DB but now the specific actor ID isn't found" + Just bye -> pure bye + Resource aid <- getJust $ localResourceID bye + a <- getJust aid + let byk' = resourceToActor byk + return $ Left (byk', a) + Right (PermitTopicExtendRemote _ enableID _) -> do + PermitTopicEnableRemote _ topicID _ <- getJust enableID + PermitTopicRemote _ remoteActorID <- getJust topicID + remoteActor <- getJust remoteActorID + remoteObject <- getJust $ remoteActorIdent remoteActor + inztance <- getJust $ remoteObjectInstance remoteObject + return $ Right (inztance, remoteObject, remoteActor) + personalOverview :: Entity Person -> Handler Html personalOverview (Entity pid person) = do (permits, invites) <- runDB $ do permits <- do + locals <- do ls <- E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable) -> do E.on $ topic E.^. PermitTopicLocalId E.==. enable E.^. PermitTopicEnableLocalTopic @@ -309,13 +339,37 @@ getHomeR = do , info ) return - ( gestureID - , role - , delegator + ( role , localActorType topic , Left (topic, actor) - , exts + , Left (delegator, exts) ) + + localsExt <- do + ls <- E.select $ E.from $ \ (permit `E.InnerJoin` gesture `E.InnerJoin` send `E.InnerJoin` extend `E.InnerJoin` resource) -> do + E.on $ extend E.^. PermitTopicExtendId E.==. resource E.^. PermitTopicExtendResourceLocalPermit + E.on $ send E.^. PermitPersonSendDelegatorId E.==. extend E.^. PermitTopicExtendPermit + E.on $ gesture E.^. PermitPersonGestureId E.==. send E.^. PermitPersonSendDelegatorPermit + E.on $ permit E.^. PermitId E.==. gesture E.^. PermitPersonGesturePermit + E.where_ $ permit E.^. PermitPerson E.==. E.val pid + return + ( resource E.^. PermitTopicExtendResourceLocalResource + , permit E.^. PermitId + , extend E.^. PermitTopicExtendId + , extend E.^. PermitTopicExtendRole + ) + for ls $ \ (E.Value resourceID, E.Value permitID, E.Value extendID, E.Value role) -> do + Resource actorID <- getJust resourceID + actor <- getJust actorID + topic <- resourceToActor <$> getLocalResource resourceID + via <- getVia extendID + return + ( role + , localActorType topic + , Left (topic, actor) + , Right via + ) + remotes <- do rs <- E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable) -> do E.on $ topic E.^. PermitTopicRemoteId E.==. enable E.^. PermitTopicEnableRemoteTopic @@ -344,14 +398,39 @@ getHomeR = do info <- getExtInfo $ Right extID return (u, info) return - ( gestureID - , role - , delegator + ( role , remoteActorType remoteActor , Right (inztance, remoteObject, remoteActor) - , exts + , Left (delegator, exts) ) - return $ locals ++ remotes + + remotesExt <- do + rs <- E.select $ E.from $ \ (permit `E.InnerJoin` gesture `E.InnerJoin` send `E.InnerJoin` extend `E.InnerJoin` resource) -> do + E.on $ extend E.^. PermitTopicExtendId E.==. resource E.^. PermitTopicExtendResourceRemotePermit + E.on $ send E.^. PermitPersonSendDelegatorId E.==. extend E.^. PermitTopicExtendPermit + E.on $ gesture E.^. PermitPersonGestureId E.==. send E.^. PermitPersonSendDelegatorPermit + E.on $ permit E.^. PermitId E.==. gesture E.^. PermitPersonGesturePermit + E.where_ $ permit E.^. PermitPerson E.==. E.val pid + return + ( resource E.^. PermitTopicExtendResourceRemoteActor + , permit E.^. PermitId + , extend E.^. PermitTopicExtendId + , extend E.^. PermitTopicExtendRole + ) + for rs $ \ (E.Value remoteActorID, E.Value permitID, E.Value extendID, E.Value role) -> do + remoteActor <- getJust remoteActorID + remoteObject <- getJust $ remoteActorIdent remoteActor + inztance <- getJust $ remoteObjectInstance remoteObject + via <- getVia extendID + return + ( role + , remoteActorType remoteActor + , Right (inztance, remoteObject, remoteActor) + , Right via + ) + + return $ locals ++ localsExt ++ remotes ++ remotesExt + invites <- do locals <- do ls <- E.select $ E.from $ \ (permit `E.InnerJoin` fulfills `E.InnerJoin` topic `E.LeftOuterJoin` enable `E.LeftOuterJoin` valid `E.LeftOuterJoin` accept) -> do @@ -418,8 +497,9 @@ getHomeR = do ) return $ sortOn (view _1) $ locals ++ remotes return (permits, invites) - let (people, repos, decks, looms, projects, groups, factories, others) = - partitionByActorType (view _4) (view _1) permits + let grabU (i, ro, _ra) = (instanceHost i, remoteObjectIdent ro) + (people, repos, decks, looms, projects, groups, factories, others) = + partitionByActorType (view _2) (bimap fst grabU . view _3) permits if null people then pure () else error "Bug: Person as a PermitTopic" @@ -447,7 +527,7 @@ getHomeR = do x = deleteFirstsBy ((==) `on` key) xs (p ++ r ++ d ++ l ++ j ++ g ++ f) in (p, r, d, l, j, g, f, x) - item (_gestureID, role, deleg, _typ, actor, exts) = + item (role, _typ, actor, Left (deleg, exts)) = [whamlet| <span> [ @@ -475,6 +555,16 @@ getHomeR = do $of Right t [ ^{actorLinkFedW t} ] |] + item (role, _typ, actor, Right via) = + [whamlet| + <span> + [ + #{show role} + ] # + ^{actorLinkFedW actor} + \ via # + ^{actorLinkFedW via} + |] invite (_fulfillsID, role, valid, accept, fulfillsHash, actor) = [whamlet| diff --git a/src/Vervis/Persist/Collab.hs b/src/Vervis/Persist/Collab.hs index 0590dc6..4db8752 100644 --- a/src/Vervis/Persist/Collab.hs +++ b/src/Vervis/Persist/Collab.hs @@ -56,6 +56,7 @@ module Vervis.Persist.Collab , getGrantActivityBody , getPermitsForResource + , getPermitsForResources , getCapability , getStems @@ -1322,6 +1323,136 @@ getPermitsForResource personID actor = do return (uExt, role, Just via) return $ directsFinal ++ extsFinal +getPermitsForResources + :: MonadIO m + => PersonId + -> Maybe [ResourceId] + -> Maybe [RemoteActorId] + -> AP.Role + -> ReaderT SqlBackend m + [ ( Either ResourceId RemoteActorId + , Either (LocalActorBy Key, OutboxItemId) FedURI + , AP.Role + , Maybe + (Either + (LocalActorBy Key, Actor) + (Instance, RemoteObject, RemoteActor) + ) + ) + ] +getPermitsForResources personID localTopics remoteTopics minRole = do + + directsLocal <- + E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable) -> do + E.on $ topic E.^. PermitTopicLocalId E.==. enable E.^. PermitTopicEnableLocalTopic + E.on $ permit E.^. PermitId E.==. topic E.^. PermitTopicLocalPermit + E.where_ $ + permit E.^. PermitPerson E.==. E.val personID E.&&. + permit E.^. PermitRole `E.in_` E.valList [minRole .. maxBound] + for_ localTopics $ \ ids -> + E.where_ $ topic E.^. PermitTopicLocalTopic `E.in_` E.valList ids + return + ( topic E.^. PermitTopicLocalTopic + , permit E.^. PermitRole + , enable E.^. PermitTopicEnableLocalGrant + ) + directsRemote <- + E.select $ E.from $ \ (permit `E.InnerJoin` topic `E.InnerJoin` enable) -> do + E.on $ topic E.^. PermitTopicRemoteId E.==. enable E.^. PermitTopicEnableRemoteTopic + E.on $ permit E.^. PermitId E.==. topic E.^. PermitTopicRemotePermit + E.where_ $ + permit E.^. PermitPerson E.==. E.val personID E.&&. + permit E.^. PermitRole `E.in_` E.valList [minRole .. maxBound] + for_ remoteTopics $ \ ids -> + E.where_ $ topic E.^. PermitTopicRemoteActor `E.in_` E.valList ids + return + ( topic E.^. PermitTopicRemoteActor + , permit E.^. PermitRole + , enable E.^. PermitTopicEnableRemoteGrant + ) + + directsLocalFinal <- + for directsLocal $ \ (E.Value resourceID, E.Value role, E.Value grantID) -> do + lr <- getLocalResource resourceID + let la = resourceToActor lr + return (Left resourceID, Left (la, grantID), role, Nothing) + directsRemoteFinal <- + for directsRemote $ \ (E.Value actorID, E.Value role, E.Value grantID) -> do + grant <- getJust grantID + u <- getRemoteActivityURI grant + return (Right actorID, Right u, role, Nothing) + + extsLocal <- + fmap (map $ over _1 Left) $ + E.select $ E.from $ \ (permit `E.InnerJoin` gesture `E.InnerJoin` send `E.InnerJoin` extend `E.InnerJoin` resource) -> do + E.on $ extend E.^. PermitTopicExtendId E.==. resource E.^. PermitTopicExtendResourceLocalPermit + E.on $ send E.^. PermitPersonSendDelegatorId E.==. extend E.^. PermitTopicExtendPermit + E.on $ gesture E.^. PermitPersonGestureId E.==. send E.^. PermitPersonSendDelegatorPermit + E.on $ permit E.^. PermitId E.==. gesture E.^. PermitPersonGesturePermit + E.where_ $ + permit E.^. PermitPerson E.==. E.val personID E.&&. + extend E.^. PermitTopicExtendRole `E.in_` E.valList [minRole .. maxBound] + for_ localTopics $ \ ids -> + E.where_ $ resource E.^. PermitTopicExtendResourceLocalResource `E.in_` E.valList ids + return + ( resource E.^. PermitTopicExtendResourceLocalResource + , permit E.^. PermitId + , extend E.^. PermitTopicExtendId + , extend E.^. PermitTopicExtendRole + ) + extsRemote <- + fmap (map $ over _1 Right) $ + E.select $ E.from $ \ (permit `E.InnerJoin` gesture `E.InnerJoin` send `E.InnerJoin` extend `E.InnerJoin` resource) -> do + E.on $ extend E.^. PermitTopicExtendId E.==. resource E.^. PermitTopicExtendResourceRemotePermit + E.on $ send E.^. PermitPersonSendDelegatorId E.==. extend E.^. PermitTopicExtendPermit + E.on $ gesture E.^. PermitPersonGestureId E.==. send E.^. PermitPersonSendDelegatorPermit + E.on $ permit E.^. PermitId E.==. gesture E.^. PermitPersonGesturePermit + E.where_ $ + permit E.^. PermitPerson E.==. E.val personID E.&&. + extend E.^. PermitTopicExtendRole `E.in_` E.valList [minRole .. maxBound] + for_ remoteTopics $ \ ids -> + E.where_ $ resource E.^. PermitTopicExtendResourceRemoteActor `E.in_` E.valList ids + return + ( resource E.^. PermitTopicExtendResourceRemoteActor + , permit E.^. PermitId + , extend E.^. PermitTopicExtendId + , extend E.^. PermitTopicExtendRole + ) + extsFinal <- + for (extsLocal ++ extsRemote) $ \ (resource, E.Value permitID, E.Value extendID, E.Value role) -> do + sender <- + requireEitherAlt + (getValBy $ UniquePermitTopicExtendLocal extendID) + (getValBy $ UniquePermitTopicExtendRemote extendID) + "PermitTopicExtend* neither" + "PermitTopicExtend* both" + (uExt, via) <- + case sender of + Left (PermitTopicExtendLocal _ enableID grantID) -> do + PermitTopicEnableLocal _ topicID _ <- getJust enableID + byk <- getPermitTopicLocal topicID + bye <- do + m <- getLocalResourceEntity byk + case m of + Nothing -> error "I just found this PermitTopicLocal in DB but now the specific actor ID isn't found" + Just bye -> pure bye + rid <- grabLocalResourceID bye + Resource aid <- getJust rid + a <- getJust aid + let byk' = resourceToActor byk + return (Left (byk', grantID), Left (byk', a)) + Right (PermitTopicExtendRemote _ enableID grantID) -> do + PermitTopicEnableRemote _ topicID _ <- getJust enableID + PermitTopicRemote _ remoteActorID <- getJust topicID + remoteActor <- getJust remoteActorID + remoteObject <- getJust $ remoteActorIdent remoteActor + inztance <- getJust $ remoteObjectInstance remoteObject + grant <- getJust grantID + u <- getRemoteActivityURI grant + return (Right u, Right (inztance, remoteObject, remoteActor)) + return (bimap E.unValue E.unValue resource, uExt, role, Just via) + return $ directsLocalFinal ++ directsRemoteFinal ++ extsFinal + getCapability :: MonadIO m => PersonId