Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2-features/pr-1980
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Revoke guest links if feature is disabled. If the guest links team feature is disabled `get /conversations/join`, `post /conversations/:cnv/code`, and `get /conversations/:cnv/code` will return an error.
33 changes: 22 additions & 11 deletions services/galley/src/Galley/API/Query.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Galley.API.Query
internalGetMemberH,
getConversationMetaH,
getConversationByReusableCode,
ensureGuestLinksEnabled,
)
where

Expand Down Expand Up @@ -513,9 +514,8 @@ getConversationByReusableCode ::
getConversationByReusableCode lusr key value = do
c <- verifyReusableCode (ConversationCode key value Nothing)
conv <- ensureConversationAccess (tUnqualified lusr) (Data.codeConversation c) CodeAccess
getFeatureStatus conv >>= \case
TeamFeatureEnabled -> pure $ coverView conv
TeamFeatureDisabled -> throw GuestLinksDisabled
ensureGuestLinksEnabled conv
pure $ coverView conv
where
coverView :: Data.Conversation -> ConversationCoverView
coverView conv =
Expand All @@ -524,12 +524,23 @@ getConversationByReusableCode lusr key value = do
cnvCoverName = Data.convName conv
}

-- FUTUREWORK(leif): refactor and make it consistent for all team features
ensureGuestLinksEnabled ::
forall r.
( Member (Error ConversationError) r,
Member TeamFeatureStore r,
Member (Input Opts) r
) =>
Data.Conversation ->
Sem r ()
ensureGuestLinksEnabled conv = do
defaultStatus <- getDefaultFeatureStatus
maybeFeatureStatus <- join <$> TeamFeatures.getFeatureStatusNoConfig @'TeamFeatureGuestLinks `traverse` Data.convTeam conv
case maybe defaultStatus tfwoStatus maybeFeatureStatus of
TeamFeatureEnabled -> pure ()
TeamFeatureDisabled -> throw GuestLinksDisabled
where
getDefaultFeatureStatus :: Sem r TeamFeatureStatusValue
getDefaultFeatureStatus =
input <&> view (optSettings . setFeatureFlags . flagConversationGuestLinks . unDefaults . to tfwoapsStatus)

getFeatureStatus :: Data.Conversation -> Sem r TeamFeatureStatusValue
getFeatureStatus conv = do
defaultStatus <- getDefaultFeatureStatus
maybeFeatureStatus <- join <$> TeamFeatures.getFeatureStatusNoConfig @'TeamFeatureGuestLinks `traverse` Data.convTeam conv
pure $ maybe defaultStatus tfwoStatus maybeFeatureStatus
getDefaultFeatureStatus = do
status <- input <&> view (optSettings . setFeatureFlags . flagConversationGuestLinks . unDefaults)
pure $ tfwoapsStatus status
78 changes: 41 additions & 37 deletions services/galley/src/Galley/API/Update.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import Galley.API.Error
import Galley.API.LegalHold.Conflicts
import Galley.API.Mapping
import Galley.API.Message
import qualified Galley.API.Query as Query
import Galley.API.Util
import qualified Galley.Data.Conversation as Data
import Galley.Data.Services as Data
Expand Down Expand Up @@ -522,16 +523,17 @@ getUpdateResult :: Sem (Error NoChanges ': r) a -> Sem r (UpdateResult a)
getUpdateResult = fmap (either (const Unchanged) Updated) . runError

addCodeH ::
Members
'[ CodeStore,
ConversationStore,
Error ConversationError,
ExternalAccess,
GundeckAccess,
Input (Local ()),
Input UTCTime
]
r =>
forall r.
( Member CodeStore r,
Member ConversationStore r,
Member (Error ConversationError) r,
Member ExternalAccess r,
Member GundeckAccess r,
Member (Input (Local ())) r,
Member (Input UTCTime) r,
Member (Input Opts) r,
Member TeamFeatureStore r
) =>
UserId ::: ConnId ::: ConvId ->
Sem r Response
addCodeH (usr ::: zcon ::: cnv) = do
Expand All @@ -547,21 +549,22 @@ data AddCodeResult

addCode ::
forall r.
Members
'[ CodeStore,
ConversationStore,
Error ConversationError,
ExternalAccess,
GundeckAccess,
Input UTCTime
]
r =>
( Member CodeStore r,
Member ConversationStore r,
Member (Error ConversationError) r,
Member ExternalAccess r,
Member GundeckAccess r,
Member (Input UTCTime) r,
Member (Input Opts) r,
Member TeamFeatureStore r
) =>
Local UserId ->
ConnId ->
Local ConvId ->
Sem r AddCodeResult
addCode lusr zcon lcnv = do
conv <- E.getConversation (tUnqualified lcnv) >>= note ConvNotFound
Query.ensureGuestLinksEnabled conv
ensureConvMember (Data.convLocalMembers conv) (tUnqualified lusr)
ensureAccess conv CodeAccess
let (bots, users) = localBotsAndUsers $ Data.convLocalMembers conv
Expand All @@ -582,8 +585,7 @@ addCode lusr zcon lcnv = do
where
createCode :: Code -> Sem r ConversationCode
createCode code = do
urlPrefix <- E.getConversationCodeURI
return $ mkConversationCode (codeKey code) (codeValue code) urlPrefix
mkConversationCode (codeKey code) (codeValue code) <$> E.getConversationCodeURI

rmCodeH ::
Members
Expand Down Expand Up @@ -631,32 +633,35 @@ rmCode lusr zcon lcnv = do
pure event

getCodeH ::
Members
'[ CodeStore,
ConversationStore,
Error CodeError,
Error ConversationError
]
r =>
forall r.
( Member CodeStore r,
Member ConversationStore r,
Member (Error CodeError) r,
Member (Error ConversationError) r,
Member (Input Opts) r,
Member TeamFeatureStore r
) =>
UserId ::: ConvId ->
Sem r Response
getCodeH (usr ::: cnv) =
setStatus status200 . json <$> getCode usr cnv

getCode ::
Members
'[ CodeStore,
ConversationStore,
Error CodeError,
Error ConversationError
]
r =>
forall r.
( Member CodeStore r,
Member ConversationStore r,
Member (Error CodeError) r,
Member (Error ConversationError) r,
Member (Input Opts) r,
Member TeamFeatureStore r
) =>
UserId ->
ConvId ->
Sem r Public.ConversationCode
getCode usr cnv = do
conv <-
E.getConversation cnv >>= note ConvNotFound
Query.ensureGuestLinksEnabled conv
ensureAccess conv CodeAccess
ensureConvMember (Data.convLocalMembers conv) usr
key <- E.makeKey cnv
Expand All @@ -665,8 +670,7 @@ getCode usr cnv = do

returnCode :: Member CodeStore r => Code -> Sem r Public.ConversationCode
returnCode c = do
urlPrefix <- E.getConversationCodeURI
pure $ Public.mkConversationCode (codeKey c) (codeValue c) urlPrefix
Public.mkConversationCode (codeKey c) (codeValue c) <$> E.getConversationCodeURI

checkReusableCodeH ::
Members '[CodeStore, Error CodeError, WaiRoutes] r =>
Expand Down
38 changes: 38 additions & 0 deletions services/galley/test/integration/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ tests s =
test s "cannot join private conversation" postJoinConvFail,
test s "revoke guest links for team conversation" testJoinTeamConvGuestLinksDisabled,
test s "revoke guest links for non-team conversation" testJoinNonTeamConvGuestLinksDisabled,
test s "get code rejected if guest links disabled" testGetCodeRejectedIfGuestLinksDisabled,
test s "post code rejected if guest links disabled" testPostCodeRejectedIfGuestLinksDisabled,
test s "remove user with only local convs" removeUserNoFederation,
test s "remove user with local and remote convs" removeUser,
test s "iUpsertOne2OneConversation" testAllOne2OneConversationRequests,
Expand Down Expand Up @@ -1241,6 +1243,42 @@ testJoinCodeConv = do
getJoinCodeConv eve (conversationKey cCode) (conversationCode cCode) !!! do
const 403 === statusCode

testGetCodeRejectedIfGuestLinksDisabled :: TestM ()
testGetCodeRejectedIfGuestLinksDisabled = do
galley <- view tsGalley
(owner, teamId, []) <- Util.createBindingTeamWithNMembers 0
let createConvWithGuestLink = do
convId <- decodeConvId <$> postTeamConv teamId owner [] (Just "testConversation") [CodeAccess] (Just ActivatedAccessRole) Nothing
void $ decodeConvCodeEvent <$> postConvCode owner convId
pure convId
convId <- createConvWithGuestLink
let checkGetCode expectedStatus = getConvCode owner convId !!! statusCode === const expectedStatus
let setStatus tfStatus =
TeamFeatures.putTeamFeatureFlagWithGalley @'Public.TeamFeatureGuestLinks galley owner teamId (Public.TeamFeatureStatusNoConfig tfStatus) !!! do
const 200 === statusCode

checkGetCode 200
setStatus Public.TeamFeatureDisabled
checkGetCode 409
setStatus Public.TeamFeatureEnabled
checkGetCode 200

testPostCodeRejectedIfGuestLinksDisabled :: TestM ()
testPostCodeRejectedIfGuestLinksDisabled = do
galley <- view tsGalley
(owner, teamId, []) <- Util.createBindingTeamWithNMembers 0
convId <- decodeConvId <$> postTeamConv teamId owner [] (Just "testConversation") [CodeAccess] (Just ActivatedAccessRole) Nothing
let checkPostCode expectedStatus = postConvCode owner convId !!! statusCode === const expectedStatus
let setStatus tfStatus =
TeamFeatures.putTeamFeatureFlagWithGalley @'Public.TeamFeatureGuestLinks galley owner teamId (Public.TeamFeatureStatusNoConfig tfStatus) !!! do
const 200 === statusCode

checkPostCode 201
setStatus Public.TeamFeatureDisabled
checkPostCode 409
setStatus Public.TeamFeatureEnabled
checkPostCode 200

testJoinTeamConvGuestLinksDisabled :: TestM ()
testJoinTeamConvGuestLinksDisabled = do
galley <- view tsGalley
Expand Down