Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7969711
Refactor effects around password resetting
Apr 22, 2022
b943d47
Add a changelog
Apr 25, 2022
c28c6eb
Code formatting
May 20, 2022
2a3055d
Add the logging effect to Brig
May 26, 2022
866dcc5
Introduce the UserQuery effect
May 24, 2022
b9d94af
Implement the getName action (replace lookupName)
May 24, 2022
e2271c1
Implement the getLocale action (was lookupLocale)
May 24, 2022
8775800
Define the BudgetStore effect (to replace Brig.Budget)
May 25, 2022
f5cbebf
Implement the getAuthentication action
May 25, 2022
e0547e2
Removing MonadReader in the UserKey module
Jun 3, 2022
c7c7f57
WIP: Almost compiles with getUserAuthentication or something like it
Jun 7, 2022
41835f0
Lots of new effects
Jun 15, 2022
24dc4ef
Interpretation for an ActivationKeyStore action
Jun 15, 2022
21b2822
It compiles (though with undefined's and commented out code)
Jun 15, 2022
708fde4
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Aug 12, 2022
9b79b8d
Get rid of one undefined
Aug 12, 2022
142e740
Drop undefined in ssoLogin
Aug 12, 2022
980342d
Drop undefined from Brig.API.User.mkActivationKey
Aug 12, 2022
011b2c3
Add back code for Birg.User.Auth.legalHoldLogin
Aug 12, 2022
419a63e
Move a utility function
Aug 12, 2022
3b95148
Define the GundeckAccess effect
Aug 12, 2022
8930372
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Aug 18, 2022
69da294
Use the GundeckAccess effect
Aug 18, 2022
1153327
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Aug 25, 2022
08adb08
Remove temporary Polysemy crutches
Aug 25, 2022
3f0b894
Handle a TODO in Brig.API.User.deleteAccount
Aug 26, 2022
975d3ef
Move an error interpreter into a separate module
Aug 26, 2022
7b31840
Change TODO to FUTUREWORK notes
Aug 26, 2022
556b850
Remove commented out code in Brig.IO.Intra
Aug 26, 2022
7212fa9
Fix missing Cql instances for activation types
Aug 26, 2022
3ee9aac
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Aug 30, 2022
19df7e1
Move the FireAndForget effect from Galley into zoo
Aug 30, 2022
48c289b
Simplify the interface of Brig.IO.Intra.notify
Aug 30, 2022
be904d9
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Sep 19, 2022
dc55d2e
Resolve a non-TODO
Sep 19, 2022
f105822
Remove the Polysemy.Async effect
Sep 19, 2022
eef87c2
Replace List1 with NonEmpty
Sep 20, 2022
1428418
Remove some in-line effect interpretations
Sep 20, 2022
b462175
Improve the interface for a Twilio effect action
Sep 20, 2022
d29eefd
Linting of BudgetStore
Sep 20, 2022
6a1379f
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Sep 27, 2022
f5e9ed0
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Sep 30, 2022
0f69444
Polysemise Brig.Provider.API.deleteBot
Oct 4, 2022
b23a3f6
Merge remote-tracking branch 'origin/develop' into polysemy/brig/user…
Oct 5, 2022
a41b0fb
Add a TODO to replace the Async effect
Oct 5, 2022
fd08303
Fix one use site of deleteBot
Oct 5, 2022
a3b9a93
Propagate effect constraints (compiles again)
Oct 5, 2022
995ac55
Use a ClientStore action instead of its interpretation
Oct 6, 2022
fa52ece
Introduce the CookieStore effect
Oct 6, 2022
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/user-effects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the UserQuery and supporting effects
81 changes: 81 additions & 0 deletions libs/brig-types/src/Brig/Types/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ module Brig.Types.Common
isValidPhonePrefix,
allPrefixes,
ExcludedPrefix (..),

-- * misc
foldKey,
keyText,
mkPhoneKey,
mkEmailKey,
EmailKey (..),
PhoneKey (..),
UserKey (..),
)
where

Expand All @@ -39,6 +48,7 @@ import Data.ByteString.Conversion
import qualified Data.Text as Text
import Data.Time.Clock (NominalDiffTime)
import Imports
import Wire.API.User.Identity

------------------------------------------------------------------------------
--- PhoneBudgetTimeout
Expand Down Expand Up @@ -111,3 +121,74 @@ instance FromJSON ExcludedPrefix where

instance ToJSON ExcludedPrefix where
toJSON (ExcludedPrefix p c) = object ["phone_prefix" .= p, "comment" .= c]

-------------------------------------------------------------------------------
-- Unique Keys

-- | An 'EmailKey' is an 'Email' in a form that serves as a unique lookup key.
data EmailKey = EmailKey
{ emailKeyUniq :: !Text,
emailKeyOrig :: !Email
}

instance Show EmailKey where
showsPrec _ = shows . emailKeyUniq

instance Eq EmailKey where
(EmailKey k _) == (EmailKey k' _) = k == k'

-- | Turn an 'Email' into an 'EmailKey'.
--
-- The following transformations are performed:
--
-- * Both local and domain parts are forced to lowercase to make
Copy link
Contributor

Choose a reason for hiding this comment

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

While it isn't common, the local part is "domain dependent" and can be case-sensitive. We probably shouldn't case fold it by default, though we can if we know the domain does handle the local part case-insensitively.

Case-folding by default will make for some very hard to trace issues if the domain does use case-sensitive local parts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just like the rest in this module, this is not new code added by me in the PR; rather, it's just being reorganised and moved from other modules. That said, I don't think this PR should be about addressing your concern and I believe you should open a ticket and bring it to the team's attention.

-- e-mail addresses fully case-insensitive.
-- * "+" suffixes on the local part are stripped unless the domain
Copy link
Contributor

Choose a reason for hiding this comment

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

This is also far from universal. If we are modeling off of GMail we would also need to strip all . characters.

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 not new code added by me in this PR; rather, it's old code I moved around.

-- part is contained in a trusted whitelist.
mkEmailKey :: Email -> EmailKey
mkEmailKey orig@(Email localPart domain) =
let uniq = Text.toLower localPart' <> "@" <> Text.toLower domain
in EmailKey uniq orig
where
localPart'
| domain `notElem` trusted = Text.takeWhile (/= '+') localPart
| otherwise = localPart
trusted = ["wearezeta.com", "wire.com", "simulator.amazonses.com"]

data PhoneKey = PhoneKey
{ -- | canonical form of 'phoneKeyOrig', without whitespace.
phoneKeyUniq :: !Text,
-- | phone number with whitespace.
phoneKeyOrig :: !Phone
}

instance Show PhoneKey where
showsPrec _ = shows . phoneKeyUniq

instance Eq PhoneKey where
(PhoneKey k _) == (PhoneKey k' _) = k == k'

mkPhoneKey :: Phone -> PhoneKey
mkPhoneKey orig =
let uniq = Text.filter (not . isSpace) (fromPhone orig)
in PhoneKey uniq orig

-- | A natural identifier (i.e. unique key) of a user.
data UserKey
= UserEmailKey !EmailKey
| UserPhoneKey !PhoneKey

instance Eq UserKey where
(UserEmailKey k) == (UserEmailKey k') = k == k'
(UserPhoneKey k) == (UserPhoneKey k') = k == k'
_ == _ = False

-- | Get the normalised text of a 'UserKey'.
keyText :: UserKey -> Text
keyText (UserEmailKey k) = emailKeyUniq k
keyText (UserPhoneKey k) = phoneKeyUniq k

foldKey :: (Email -> a) -> (Phone -> a) -> UserKey -> a
foldKey f g k = case k of
UserEmailKey ek -> f (emailKeyOrig ek)
UserPhoneKey pk -> g (phoneKeyOrig pk)
9 changes: 6 additions & 3 deletions libs/polysemy-wire-zoo/polysemy-wire-zoo.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ build-type: Simple
library
exposed-modules:
Polysemy.TinyLog
Wire.Sem.Concurrency
Wire.Sem.Concurrency.IO
Wire.Sem.Concurrency.Sequential
Wire.Sem.Error
Wire.Sem.FireAndForget
Wire.Sem.FireAndForget.IO
Wire.Sem.FromUTC
Wire.Sem.Logger
Wire.Sem.Logger.Level
Expand All @@ -25,9 +31,6 @@ library
Wire.Sem.Paging.Cassandra
Wire.Sem.Random
Wire.Sem.Random.IO
Wire.Sem.Concurrency
Wire.Sem.Concurrency.IO
Wire.Sem.Concurrency.Sequential

other-modules: Paths_polysemy_wire_zoo
hs-source-dirs: src
Expand Down
37 changes: 37 additions & 0 deletions libs/polysemy-wire-zoo/src/Wire/Sem/Error.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Wire.Sem.Error where

import Imports
import Polysemy
import Polysemy.Error
import qualified UnliftIO.Exception as UnliftIO
import Wire.API.Error

interpretErrorToException ::
(Exception exc, Member (Embed IO) r) =>
(err -> exc) ->
Sem (Error err ': r) a ->
Sem r a
interpretErrorToException f = either (embed @IO . UnliftIO.throwIO . f) pure <=< runError

interpretWaiErrorToException ::
(APIError e, Member (Embed IO) r) =>
Sem (Error e ': r) a ->
Sem r a
interpretWaiErrorToException = interpretErrorToException toWai
31 changes: 31 additions & 0 deletions libs/polysemy-wire-zoo/src/Wire/Sem/FireAndForget.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{-# LANGUAGE TemplateHaskell #-}

-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Wire.Sem.FireAndForget where

import Polysemy

data FireAndForget m a where
FireAndForgetOne :: m () -> FireAndForget m ()
SpawnMany :: [m ()] -> FireAndForget m ()

makeSem ''FireAndForget

fireAndForget :: Member FireAndForget r => Sem r () -> Sem r ()
fireAndForget = fireAndForgetOne
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{-# LANGUAGE TemplateHaskell #-}

-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
Expand All @@ -17,33 +15,22 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.Effects.FireAndForget
( FireAndForget,
fireAndForget,
spawnMany,
interpretFireAndForget,
)
where
module Wire.Sem.FireAndForget.IO (interpretFireAndForget) where

import Imports
import Polysemy
import Polysemy.Final
import UnliftIO.Async (pooledMapConcurrentlyN_)

data FireAndForget m a where
FireAndForgetOne :: m () -> FireAndForget m ()
SpawnMany :: [m ()] -> FireAndForget m ()

makeSem ''FireAndForget

fireAndForget :: Member FireAndForget r => Sem r () -> Sem r ()
fireAndForget = fireAndForgetOne
import Wire.Sem.FireAndForget

-- | Run actions in separate threads and ignore results.
--
-- /Note/: this will also ignore any state and error effects contained in the
-- 'FireAndForget' action. Use with care.
interpretFireAndForget :: Member (Final IO) r => Sem (FireAndForget ': r) a -> Sem r a
interpretFireAndForget ::
Member (Final IO) r =>
Sem (FireAndForget ': r) a ->
Sem r a
interpretFireAndForget = interpretFinal @IO $ \case
FireAndForgetOne action -> do
action' <- runS action
Expand Down
34 changes: 33 additions & 1 deletion services/brig/brig.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ library
Brig.AWS
Brig.AWS.SesNotification
Brig.AWS.Types
Brig.Budget
Brig.Calling
Brig.Calling.API
Brig.Calling.Internal
Expand All @@ -55,20 +54,49 @@ library
Brig.Data.Types
Brig.Data.User
Brig.Data.UserKey
Brig.Effects.ActivationKeyStore
Brig.Effects.ActivationKeyStore.Cassandra
Brig.Effects.ActivationSupply
Brig.Effects.ActivationSupply.IO
Brig.Effects.BlacklistPhonePrefixStore
Brig.Effects.BlacklistPhonePrefixStore.Cassandra
Brig.Effects.BlacklistStore
Brig.Effects.BlacklistStore.Cassandra
Brig.Effects.BudgetStore
Brig.Effects.BudgetStore.Cassandra
Brig.Effects.ClientStore
Brig.Effects.ClientStore.Cassandra
Brig.Effects.CodeStore
Brig.Effects.CodeStore.Cassandra
Brig.Effects.Common
Brig.Effects.CookieStore
Brig.Effects.CookieStore.Cassandra
Brig.Effects.Delay
Brig.Effects.GalleyAccess
Brig.Effects.GalleyAccess.Http
Brig.Effects.GundeckAccess
Brig.Effects.GundeckAccess.Http
Brig.Effects.JwtTools
Brig.Effects.PasswordResetStore
Brig.Effects.PasswordResetStore.CodeStore
Brig.Effects.PasswordResetSupply
Brig.Effects.PasswordResetSupply.IO
Brig.Effects.PublicKeyBundle
Brig.Effects.SFT
Brig.Effects.Twilio
Brig.Effects.Twilio.IO
Brig.Effects.UniqueClaimsStore
Brig.Effects.UniqueClaimsStore.Cassandra
Brig.Effects.UserHandleStore
Brig.Effects.UserHandleStore.Cassandra
Brig.Effects.UserKeyStore
Brig.Effects.UserKeyStore.Cassandra
Brig.Effects.UserPendingActivationStore
Brig.Effects.UserPendingActivationStore.Cassandra
Brig.Effects.UserQuery
Brig.Effects.UserQuery.Cassandra
Brig.Effects.VerificationCodeStore
Brig.Effects.VerificationCodeStore.Cassandra
Brig.Email
Brig.Federation.Client
Brig.Index.Eval
Expand All @@ -93,6 +121,7 @@ library
Brig.Queue.Stomp
Brig.Queue.Types
Brig.RPC
Brig.RPC.Decode
Brig.Run
Brig.SMTP
Brig.Team.API
Expand Down Expand Up @@ -229,6 +258,7 @@ library
, imports
, insert-ordered-containers
, iproute >=1.5
, iso3166-country-codes
, iso639 >=0.1
, jwt-tools
, lens >=3.8
Expand All @@ -247,7 +277,9 @@ library
, optparse-applicative >=0.11
, pem >=0.2
, polysemy
, polysemy-conc
, polysemy-plugin
, polysemy-time
, polysemy-wire-zoo
, proto-lens >=0.1
, random-shuffle >=0.0.3
Expand Down
54 changes: 51 additions & 3 deletions services/brig/src/Brig/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,71 @@ where
import Brig.API.Handler (Handler)
import qualified Brig.API.Internal as Internal
import qualified Brig.API.Public as Public
import Brig.API.Types
import Brig.Effects.ActivationKeyStore
import Brig.Effects.ActivationSupply
import Brig.Effects.BlacklistPhonePrefixStore (BlacklistPhonePrefixStore)
import Brig.Effects.BlacklistStore (BlacklistStore)
import Brig.Effects.BudgetStore
import Brig.Effects.ClientStore
import Brig.Effects.CodeStore
import Brig.Effects.CookieStore
import Brig.Effects.GalleyAccess
import Brig.Effects.GundeckAccess (GundeckAccess)
import Brig.Effects.PasswordResetStore (PasswordResetStore)
import Brig.Effects.PasswordResetSupply (PasswordResetSupply)
import Brig.Effects.Twilio
import Brig.Effects.UniqueClaimsStore
import Brig.Effects.UserHandleStore
import Brig.Effects.UserKeyStore
import Brig.Effects.UserPendingActivationStore (UserPendingActivationStore)
import Brig.Effects.UserQuery
import Brig.Effects.VerificationCodeStore
import Data.Qualified
import qualified Data.Swagger.Build.Api as Doc
import Network.Wai.Routing (Routes)
import Polysemy
import Polysemy.Async
import Polysemy.Conc.Effect.Race
import Polysemy.Error
import Polysemy.Input
import Polysemy.Resource
import qualified Polysemy.TinyLog as P
import qualified Ropes.Twilio as Twilio
import Wire.Sem.Concurrency
import Wire.Sem.Paging

sitemap ::
forall r p.
Paging p =>
Members
'[ CodeStore,
PasswordResetStore,
'[ ActivationKeyStore,
ActivationSupply,
Async,
BlacklistStore,
BlacklistPhonePrefixStore,
UserPendingActivationStore p
BudgetStore,
ClientStore,
CodeStore,
Concurrency 'Unsafe,
CookieStore,
Error ReAuthError,
Error Twilio.ErrorResponse,
GalleyAccess,
GundeckAccess,
Input (Local ()),
P.TinyLog,
PasswordResetStore,
PasswordResetSupply,
Race,
Resource,
Twilio,
UniqueClaimsStore,
UserHandleStore,
UserKeyStore,
UserPendingActivationStore p,
UserQuery p,
VerificationCodeStore
]
r =>
Routes Doc.ApiBuilder (Handler r) ()
Expand Down
Loading