Skip to content

Commit 6a2debf

Browse files
committed
Refactor: team conversation access control.
1 parent 0f6a841 commit 6a2debf

5 files changed

Lines changed: 44 additions & 11 deletions

File tree

libs/wire-api/src/Wire/API/Team/Member.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ module Wire.API.Team.Member
7171
IsPerm (..),
7272
HiddenPerm (..),
7373
mkSingleTeamMembersPage,
74+
75+
-- * TeamPrincipal
76+
TeamPrincipal,
77+
isFullTeamMember,
7478
)
7579
where
7680

@@ -657,6 +661,22 @@ collaboratorToTeamPermissions =
657661
Collaborator.ImplicitConnection -> mempty
658662
)
659663

664+
-- | A user associated with a team, either as a collaborator (@Left@) or as a
665+
-- full team member (@Right@). The 'IsPerm' instance is derived automatically
666+
-- via the 'Either' instance, using 'collaboratorToTeamPermissions' for the
667+
-- @Left@ case.
668+
type TeamPrincipal = Either TeamCollaborator TeamMember
669+
670+
-- | True only for full team members, not for collaborators.
671+
-- Used in conversation access-role checks that enforce team-member-only
672+
-- conversations, preserving the invariant that collaborators are not
673+
-- considered equivalent to full members for access control.
674+
--
675+
-- (We probably do not want to discriminate against collaborators in
676+
-- this way, but that's a semantic change for another PR.)
677+
isFullTeamMember :: TeamPrincipal -> Bool
678+
isFullTeamMember = isRight
679+
660680
----------------------------------------------------------------------
661681

662682
makeLenses ''TeamMember'

libs/wire-subsystems/src/Wire/ConversationSubsystem/Action.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,8 +794,8 @@ performConversationJoin qusr lconv (ConversationJoin invited role joinType) = do
794794
tms <-
795795
Map.fromList . map (view Wire.API.Team.Member.userId &&& Imports.id)
796796
<$> TeamSubsystem.internalSelectTeamMembers tid newUsers
797-
let userMembershipMap = map (Imports.id &&& flip Map.lookup tms) newUsers
798-
ensureAccessRole (convAccessRoles conv) userMembershipMap
797+
let userMembershipMap = map (Imports.id &&& (fmap Right . flip Map.lookup tms)) newUsers
798+
in ensureAccessRole (convAccessRoles conv) userMembershipMap
799799
ensureConnectedToLocalsOrSameTeam lusr newUsers
800800
checkLocals lusr Nothing newUsers = do
801801
ensureAccessRole (convAccessRoles conv) (map (,Nothing) newUsers)

libs/wire-subsystems/src/Wire/ConversationSubsystem/CreateInternal.hs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,8 @@ checkCreateConvPermissions lusr newConv Nothing allUsers = do
330330
ensureConnected lusr allUsers
331331
checkCreateConvPermissions lusr newConv (Just tinfo) allUsers = do
332332
let convTeam = cnvTeamId tinfo
333-
mTeamMember <- getTeamMember (tUnqualified lusr) (Just convTeam)
334-
teamAssociation <- case mTeamMember of
335-
Just tm -> pure (Just (Right tm))
336-
Nothing -> do
337-
Left <$$> internalGetTeamCollaborator convTeam (tUnqualified lusr)
333+
teamAssociation <- TeamSubsystem.lookupTeamPrincipal convTeam (tUnqualified lusr)
334+
let mTeamMember = teamAssociation >>= either (const Nothing) Just
338335

339336
let checkGroup = do
340337
void $ permissionCheck CreateConversation teamAssociation
@@ -347,7 +344,7 @@ checkCreateConvPermissions lusr newConv (Just tinfo) allUsers = do
347344
MeetingConversation -> checkGroup
348345

349346
convLocalMemberships <- mapM (flip TeamSubsystem.internalGetTeamMember convTeam) (ulLocals allUsers)
350-
ensureAccessRole (accessRoles newConv) (zip (ulLocals allUsers) convLocalMemberships)
347+
ensureAccessRole (accessRoles newConv) (zip (ulLocals allUsers) (fmap (fmap Right) convLocalMemberships))
351348
ensureConnectedToLocals (tUnqualified lusr) (notTeamMember (ulLocals allUsers) (catMaybes convLocalMemberships))
352349
ensureConnectedToRemotes lusr (ulRemotes allUsers)
353350
where

libs/wire-subsystems/src/Wire/ConversationSubsystem/Util.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ ensureAccessRole ::
115115
Member (ErrorS 'ConvAccessDenied) r
116116
) =>
117117
Set Public.AccessRole ->
118-
[(UserId, Maybe TeamMember {- isJust iff user and conv are in the same team -})] ->
118+
[(UserId, Maybe TeamPrincipal {- Just (Right tm) iff full team member, Just (Left c) iff collaborator, Nothing otherwise -})] ->
119119
Sem r ()
120120
ensureAccessRole roles users = do
121121
when (Set.null roles) $ throwS @'ConvAccessDenied
122122
unless (NonTeamMemberAccessRole `Set.member` roles) $
123-
when (any (isNothing . snd) users) $
123+
when (any (maybe True (not . isFullTeamMember) . snd) users) $
124124
throwS @'NotATeamMember
125125
unless (Set.fromList [GuestAccessRole, ServiceAccessRole] `Set.isSubsetOf` roles) $ do
126126
activated <- lookupActivatedUsers (fst <$> users)
@@ -685,7 +685,7 @@ ensureConversationAccess ::
685685
ensureConversationAccess zusr conv access = do
686686
ensureAccess conv access
687687
zusrMembership <- maybe (pure Nothing) (TeamSubsystem.internalGetTeamMember zusr) (Data.convTeam conv)
688-
ensureAccessRole (Data.convAccessRoles conv) [(zusr, zusrMembership)]
688+
ensureAccessRole (Data.convAccessRoles conv) [(zusr, fmap Right zusrMembership)]
689689

690690
ensureAccess ::
691691
(Member (ErrorS 'ConvAccessDenied) r) =>

libs/wire-subsystems/src/Wire/TeamSubsystem.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import Wire.API.Team.LegalHold (UserLegalHoldStatusResponse)
3333
import Wire.API.Team.Member
3434
import Wire.API.Team.Member.Error
3535
import Wire.API.Team.Member.Info (TeamMemberInfoList)
36+
import Wire.TeamCollaboratorsSubsystem
3637

3738
data PermissionCheckArgs teamAssociation where
3839
PermissionCheckArgs ::
@@ -144,3 +145,18 @@ checkConsent ::
144145
Sem r ConsentGiven
145146
checkConsent teamsOfUsers other = do
146147
consentGiven <$> getLHStatus (Map.lookup other teamsOfUsers) other
148+
149+
-- | Look up a user as a 'TeamPrincipal': a full member (@Right@) takes
150+
-- precedence over a collaborator (@Left@). Returns 'Nothing' if the user has
151+
-- no association with the team.
152+
lookupTeamPrincipal ::
153+
( Member TeamSubsystem r,
154+
Member TeamCollaboratorsSubsystem r
155+
) =>
156+
TeamId ->
157+
UserId ->
158+
Sem r (Maybe TeamPrincipal)
159+
lookupTeamPrincipal tid uid =
160+
internalGetTeamMember uid tid >>= \case
161+
Just m -> pure (Just (Right m))
162+
Nothing -> fmap Left <$> internalGetTeamCollaborator tid uid

0 commit comments

Comments
 (0)