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/5-internal/WPB-22549-apps-vs-convs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix: create team members for apps in galley, not just brig users.
23 changes: 21 additions & 2 deletions integration/test/Test/Apps.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module Test.Apps where

import API.Brig
import qualified API.BrigInternal as BrigI
import API.Galley
import SetupHelpers
import Testlib.Prelude

Expand Down Expand Up @@ -105,8 +106,18 @@ testCreateApp = do
foundUserType searcher exactMatchTerm aTypes =
searchContacts searcher exactMatchTerm OwnDomain `bindResponse` \resp -> do
resp.status `shouldMatchInt` 200
foundDoc <- resp.json %. "documents" >>= asList
(%. "type") `mapM` foundDoc `shouldMatch` aTypes
foundDocs :: [Value] <- resp.json %. "documents" >>= asList
docsInTeam :: [Value] <- do
-- make sure that matches from previous test runs don't get in the way.
catMaybes
<$> forM
foundDocs
( \doc -> do
tidActual <- doc %. "team" & asString
pure $ if tidActual == tid then Just doc else Nothing
)

(%. "type") `mapM` docsInTeam `shouldMatch` aTypes

-- App's user is findable from /search/contacts
BrigI.refreshIndex domain
Expand All @@ -121,6 +132,14 @@ testCreateApp = do
memberName <- regularMember %. "name" & asString
foundUserType owner memberName ["regular"]

-- GET /one2one-conversations/{usr_domain}/{usr}
getMLSOne2OneConversation regularMember appIdObject `bindResponse` \resp -> do
resp.status `shouldMatchInt` 200

-- POST /mls/key-packages/claim/{user_domain}/{user}
claimKeyPackages def regularMember appIdObject `bindResponse` \resp -> do
resp.status `shouldMatchInt` 200

testRefreshAppCookie :: (HasCallStack) => App ()
testRefreshAppCookie = do
(alice, tid, [bob]) <- createTeam OwnDomain 2
Expand Down
27 changes: 27 additions & 0 deletions integration/test/Test/Connection.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
module Test.Connection where

import API.Brig (getConnection, postConnection, putConnection)
import qualified API.Brig as Brig
import API.BrigInternal
import API.Galley
import Notifications
Expand All @@ -25,6 +26,32 @@ import Testlib.Prelude
import Testlib.VersionedFed
import UnliftIO.Async (forConcurrently_)

testAppConnection :: (HasCallStack) => App ()
testAppConnection = do
domain <- make OwnDomain
(owner, tid, [mem1, mem2]) <- createTeam domain 3

let newApp :: Brig.NewApp
newApp =
def
{ Brig.name = "chappie",
Brig.description = "some description of this app",
Brig.category = "ai"
}
app <- bindResponse (Brig.createApp owner tid newApp) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "user"

-- Users from the same team are not supposed to connect to each other
postConnection mem1 mem2 `bindResponse` \resp -> do
resp.status `shouldMatchInt` 403
resp.json %. "label" `shouldMatch` "same-binding-team-users"

-- the same goes for apps from the same team
postConnection mem1 app `bindResponse` \resp -> do
resp.status `shouldMatchInt` 404
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be 403, but not high prio issue.

resp.json %. "label" `shouldMatch` "not-found"

testConnectWithRemoteUser :: (HasCallStack) => OneOf Domain AnyFedDomain -> App ()
testConnectWithRemoteUser owningDomain = do
let otherDomain = case owningDomain of
Expand Down
52 changes: 52 additions & 0 deletions integration/test/Test/Conversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,65 @@ import Control.Monad.Reader
import qualified Data.Aeson as Aeson
import qualified Data.Text as T
import GHC.Stack
import MLS.Util
import Notifications
import SetupHelpers hiding (deleteUser)
import Testlib.One2One (generateRemoteAndConvIdWithDomain)
import Testlib.Prelude
import Testlib.ResourcePool
import Testlib.VersionedFed

testConversationWithAppOwnTeam :: (HasCallStack) => App ()
testConversationWithAppOwnTeam = do
domain <- make OwnDomain
(owner, tid, [mem1, mem2]) <- createTeam domain 3

let newApp :: NewApp
newApp =
def
{ name = "chappie",
description = "some description of this app",
category = "ai"
}
app <- bindResponse (createApp owner tid newApp) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "user"

-- proteus
do
conv <- postConversation mem1 defProteus >>= getJSON 201
addMembers mem1 conv (def {users = [mem2]}) >>= assertSuccess
addMembers mem1 conv (def {users = [app]}) >>= assertLabel 403 "access-denied" -- apps don't support proteus.

-- mls
[mem1c, mem2c, appc] <- traverse (createMLSClient def) [mem1, mem2, app]

-- mls 1:1
do
-- mem2c needs 2 key packages
traverse_ (uploadNewKeyPackage def) [mem1c, mem2c, mem2c, appc]

let runCheck :: (HasCallStack) => Value -> ClientIdentity -> Value -> App ()
runCheck from fromc to = do
conv <- getMLSOne2OneConversation from to >>= getJSON 200
convId <- objConvId $ conv %. "conversation"

createGroup def fromc convId
msg1 <- createAddCommit fromc convId [to]
void (sendAndConsumeCommitBundle msg1)

msg2 <- createApplicationMessage convId fromc "hi new guy!"
void (sendAndConsumeMessage msg2)

-- regular to regular
runCheck mem1 mem1c mem2

-- regular to app
runCheck mem1 mem1c app

-- app to regular
runCheck app appc mem2

testFederatedConversation :: (HasCallStack) => App ()
testFederatedConversation = do
-- This test was created to verify that the false positive log message:
Expand Down
10 changes: 5 additions & 5 deletions integration/test/Test/UserGroup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ testUserGroupSmoke = do

bindResponse (getUserGroup owner gid) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "members" `shouldMatch` [mem1id, mem2id]
resp.json %. "members" `shouldMatchSet` [mem1id, mem2id]

bindResponse (updateUserGroup owner badGid (object ["name" .= ""])) $ \resp -> do
resp.status `shouldMatchInt` 400
Expand Down Expand Up @@ -93,7 +93,7 @@ testUserGroupSmoke = do
bindResponse (getUserGroup owner gid) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "name" `shouldMatch` "also good"
resp.json %. "members" `shouldMatch` [mem2id, mem3id, mem4id, mem5id]
resp.json %. "members" `shouldMatchSet` [mem2id, mem3id, mem4id, mem5id]

bindResponse (getUserGroups owner def) $ \resp -> do
resp.status `shouldMatchInt` 200
Expand Down Expand Up @@ -131,7 +131,7 @@ testUserGroupSmoke = do

bindResponse (getUserGroup owner ug2Id) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "members" `shouldMatch` [mem1id]
resp.json %. "members" `shouldMatchSet` [mem1id]

bindResponse (updateUserGroupUsers owner ug2Id [mem8id, mem9id]) $ \resp -> do
resp.status `shouldMatchInt` 200
Expand All @@ -142,7 +142,7 @@ testUserGroupSmoke = do

bindResponse (getUserGroup owner ug2Id) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "members" `shouldMatch` [mem8id, mem9id]
resp.json %. "members" `shouldMatchSet` [mem8id, mem9id]

bindResponse (updateUserGroupUsers owner ug2Id []) $ \resp -> do
resp.status `shouldMatchInt` 200
Expand All @@ -153,7 +153,7 @@ testUserGroupSmoke = do

bindResponse (getUserGroup owner ug2Id) $ \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "members" `shouldMatch` ([] :: [()])
resp.json %. "members" `shouldMatchSet` ([] :: [()])

testUserGroupAddGroupDenied :: (HasCallStack) => App ()
testUserGroupAddGroupDenied = do
Expand Down
4 changes: 4 additions & 0 deletions libs/wire-api/src/Wire/API/User/Activation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ instance ToSchema Activate where
-- | Information returned as part of a successful activation.
data ActivationResponse = ActivationResponse
{ -- | The activated / verified user identity.
--
-- FUTUREWORK(fisx): this is from the times when UserIdentity was
-- user or phone. since SSO has been added to the type, we should
-- either use a different type here or change UserIdentity back.
activatedIdentity :: UserIdentity,
-- | Whether this is the first verified identity of the account.
activatedFirst :: Bool
Expand Down
4 changes: 4 additions & 0 deletions libs/wire-subsystems/src/Wire/AppSubsystem/Interpreter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Wire.AppSubsystem.Interpreter where

import Data.ByteString.Conversion
import Data.Id
import Data.Json.Util
import Data.Map qualified as Map
import Data.Qualified
import Data.RetryAfter
Expand All @@ -35,6 +36,7 @@ import System.Logger.Message qualified as Log
import Wire.API.App qualified as Apps
import Wire.API.Event.Team
import Wire.API.Team.Member qualified as T
import Wire.API.Team.Role qualified as R
import Wire.API.User
import Wire.API.User.Auth
import Wire.AppStore (AppStore, StoredApp (..))
Expand Down Expand Up @@ -116,6 +118,8 @@ createAppImpl lusr tid (Apps.NewApp new password6) = do
-- create app and user entries
Store.createApp app
Store.createUser u Nothing
now <- toUTCTimeMillis <$> get
void $ addTeamMember u.id tid (Just (tUnqualified lusr, now)) R.RoleMember
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an essential fix. Before this, apps had a user record, but no team member record. (Excercise to the reader: why was that bad?)

internalUpdateSearchIndex u.id

-- generate a team event
Expand Down
1 change: 1 addition & 0 deletions services/brig/src/Brig/API/MLS/KeyPackages.hs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ claimLocalKeyPackages qusr skipOwn suite target = do
uncurry (KeyPackageBundleEntry (tUntagged target) c)
<$> wrapClientM (Data.claimKeyPackage target c suite)

-- FUTUREWORK: shouldn't this be defined elsewhere for general use?
assertUserNotUnderLegalHold :: ExceptT ClientError (AppT r) ()
assertUserNotUnderLegalHold = do
-- this is okay because there can only be one StoredUser per UserId
Expand Down
14 changes: 13 additions & 1 deletion services/brig/src/Brig/API/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ createUser rateLimitKey new = do
User ->
StoredInvitation ->
EmailKey ->
-- if you are surprised because apps don't even have a
-- UserIdentity: we're in createUser here, so apps are not in
-- the picture.
UserIdentity ->
ExceptT RegisterError (AppT r) ()
acceptInvitationToTeam account inv uk ident = do
Expand All @@ -478,7 +481,14 @@ createUser rateLimitKey new = do
UserPendingActivationStore.remove uid
InvitationStore.deleteInvitation inv.teamId inv.invitationId

addUserToTeamSSO :: User -> TeamId -> UserIdentity -> ExceptT RegisterError (AppT r) CreateUserTeam
addUserToTeamSSO ::
User ->
TeamId ->
-- if you are surprised because apps don't even have a
-- UserIdentity: we're in createUser here, so apps are not in
-- the picture
UserIdentity ->
ExceptT RegisterError (AppT r) CreateUserTeam
addUserToTeamSSO account tid ident = do
let uid = userId account
added <- lift $ liftSem $ GalleyAPIAccess.addTeamMember uid tid Nothing defaultRole
Expand Down Expand Up @@ -746,6 +756,8 @@ onActivated (AccountActivated account) = liftSem $ do
Log.info $ field "user" (toByteString uid) . msg (val "User activated")
User.internalUpdateSearchIndex uid
Events.generateUserEvent uid Nothing $ UserActivated account
-- userIdentity is always Just at the time of writing this comment,
-- since account has been activated already.
pure (uid, userIdentity account, True)
onActivated (EmailActivated uid email) = liftSem $ do
User.internalUpdateSearchIndex uid
Expand Down
3 changes: 3 additions & 0 deletions services/brig/src/Brig/User/Auth.hs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ validateLoginId :: LoginId -> Either EmailKey Handle
validateLoginId (LoginByEmail email) = (Left . mkEmailKey) email
validateLoginId (LoginByHandle h) = Right h

-- Has a user been activated so they can login? (This does not apply
-- to apps, because they do not login, they already have their
-- cookie.)
isPendingActivation ::
forall r.
( Member UserSubsystem r,
Expand Down
2 changes: 1 addition & 1 deletion services/galley/src/Galley/API/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ import Wire.TeamStore
import Wire.TeamSubsystem (TeamSubsystem)
import Wire.TeamSubsystem qualified as TeamSubsystem

data UserType = User | Bot
data UserType = User | Bot -- FUTUREWORK: there is UserType in Wire.API.User now, should we use that? (there is also UserType variant for searcho/contacts, but there is a good reason for that one.)

userToProtectee :: UserType -> UserId -> LegalholdProtectee
userToProtectee User user = ProtectedUser user
Expand Down