Skip to content

Commit

Permalink
[stash] please reset this commit and try again more coherently.
Browse files Browse the repository at this point in the history
  • Loading branch information
fisx committed Aug 5, 2024
1 parent f348d15 commit 9168390
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 72 deletions.
1 change: 1 addition & 0 deletions docs/src/developer/reference/user/activation.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ email to arrive.
### Activating an existing account
(RefActivationSubmit)=

TODO: is POST /activate dead, or is it still used anywhere?
If the account [has not been activated during verification](RefRegistrationNoPreverification), it can be activated afterwards by submitting an activation code to `POST /activate`. Sample request and response:

```
Expand Down
10 changes: 10 additions & 0 deletions libs/wire-subsystems/src/Wire/UserSubsystem/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Wire.UserSubsystem.Error where
import Imports
import Wire.API.Error
import Wire.API.Error.Brig qualified as E
import Wire.API.User.Identity
import Wire.Error

-- | All errors that are thrown by the user subsystem are subsumed under this sum type.
Expand All @@ -16,6 +17,14 @@ data UserSubsystemError
| UserSubsystemHandleExists
| UserSubsystemInvalidHandle
| UserSubsystemProfileNotFound
| UserSubsystemChangeEmailError ChangeEmailError
deriving (Eq, Show)

data ChangeEmailError
= InvalidNewEmail !Email !String
| EmailExists !Email
| ChangeBlacklistedEmail !Email
| EmailManagedByScim
deriving (Eq, Show)

userSubsystemErrorToHttpError :: UserSubsystemError -> HttpError
Expand All @@ -28,5 +37,6 @@ userSubsystemErrorToHttpError =
UserSubsystemHandleExists -> errorToWai @E.HandleExists
UserSubsystemInvalidHandle -> errorToWai @E.InvalidHandle
UserSubsystemHandleManagedByScim -> errorToWai @E.HandleManagedByScim
UserSubsystemChangeEmailError _ -> _ -- check how this is handled in brig! is it also an api error? if not: is it ok to throw it here anyway?

instance Exception UserSubsystemError
88 changes: 83 additions & 5 deletions libs/wire-subsystems/src/Wire/UserSubsystem/Interpreter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ import Wire.API.Federation.Error
import Wire.API.Team.Feature
import Wire.API.Team.Member hiding (userId)
import Wire.API.User
import Wire.API.User.Activation
import Wire.API.UserEvent
import Wire.Arbitrary
import Wire.BlockListStore as BlockList
import Wire.BlockListStore as BlockListStore
import Wire.DeleteQueue
import Wire.EmailSubsystem
import Wire.Events
import Wire.FederationAPIAccess
import Wire.GalleyAPIAccess
Expand Down Expand Up @@ -114,19 +117,94 @@ interpretUserSubsystem = interpret \case
-- :> CanThrow 'BlacklistedEmail
-- :> CanThrow 'BadCredentials
--
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/put_access_self_email "change-self-email"
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/put_access_self_email "change-self-email" -- this is the one we're interested in for this PR.
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/post_activate_send "post-activate-send"
updateUserEmailInitImpl :: a
updateUserEmailInitImpl = undefined

-- | Call 'changeEmail' and process result: if email changes to itself, succeed, if not, send
-- validation email.
updateUserEmailInitImpl ::
forall r.
(Member BlockListStore r, Member UserKeyStore r, Member EmailSubsystem r) =>
UserId ->
Email ->
UpdateOriginType ->
Sem r ChangeEmailResponse
updateUserEmailInitImpl uid email updateOriginType = do
result <- prepareUpdateUserEmail u email updateOriginType
case result of
ChangeEmailIdempotent ->
pure ChangeEmailResponseIdempotent
ChangeEmailNeedsActivation (usr, adata, en) -> lift $ do
liftSem $ sendOutEmail usr adata en
wrapClient $ Data.updateEmailUnvalidated u email
wrapClient $ reindex u
pure ChangeEmailResponseNeedsActivation
where
sendOutEmail usr adata en = do
(maybe sendActivationMail (const sendEmailAddressUpdateMail) usr.userIdentity)
en
(userDisplayName usr)
(activationKey adata)
(activationCode adata)
(Just (userLocale usr))

-- | Prepare changing the email (checking a number of invariants).
prepareUpdateUserEmail ::
(Member BlockListStore r, Member UserKeyStore r, Member (Error ChangeEmailError) r) =>
UserId ->
Email ->
UpdateOriginType ->
Sem r ChangeEmailResult
prepareUpdateUserEmail u email updateOriginType = do
em <-
either
(throwE . InvalidNewEmail email)
pure
(validateEmail email)
let ek = mkEmailKey em
blacklisted <- BlockListStore.exists ek
when blacklisted $
throwE (ChangeBlacklistedEmail email)
available <- lift $ liftSem $ keyAvailable ek (Just u)
unless available $
throwE $
EmailExists email
usr <- maybe (throwM $ UserProfileNotFound u) pure =<< lift (wrapClient $ Data.lookupUser WithPendingInvitations u)
case emailIdentity =<< userIdentity usr of
-- The user already has an email address and the new one is exactly the same
Just current | current == em -> pure ChangeEmailIdempotent
_ -> do
unless (userManagedBy usr /= ManagedByScim || updateOriginType == UpdateOriginScim) $
throwE EmailManagedByScim
timeout <- setActivationTimeout <$> view settings
act <- lift . wrapClient $ Data.newActivation ek timeout (Just u)
pure $ ChangeEmailNeedsActivation (usr, act, em)

-- | Outcome of the invariants check in 'Brig.API.User.changeEmail'.
-- TODO: does this belong here? or in wire-api?
data ChangeEmailResult
= -- | The request was successful, user needs to verify the new email address
ChangeEmailNeedsActivation !(User, Activation, Email)
| -- | The user asked to change the email address to the one already owned
ChangeEmailIdempotent

-- | The information associated with the pending activation of a 'UserKey'.
data Activation = Activation
{ -- | An opaque key for the original 'UserKey' pending activation.
activationKey :: !ActivationKey,
-- | The confidential activation code.
activationCode :: !ActivationCode
}
deriving (Eq, Show)

-- :> CanThrow 'UserKeyExists
-- :> CanThrow 'InvalidActivationCodeWrongUser
-- :> CanThrow 'InvalidActivationCodeWrongCode
-- :> CanThrow 'InvalidEmail
-- :> CanThrow 'InvalidPhone
--
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/get_activate "get-activate"
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/post_activate "post-activate"
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/get_activate "get-activate" (is this still used?)
-- https://staging-nginz-https.zinfra.io/v5/api/swagger-ui/#/default/post_activate "post-activate" (is this still used?)
updateUserEmailCompleteImpl :: a
updateUserEmailCompleteImpl = undefined

Expand Down
7 changes: 0 additions & 7 deletions services/brig/src/Brig/API/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,6 @@ data CreateUserTeam = CreateUserTeam
}
deriving (Show)

-- | Outcome of the invariants check in 'Brig.API.User.changeEmail'.
data ChangeEmailResult
= -- | The request was successful, user needs to verify the new email address
ChangeEmailNeedsActivation !(User, Activation, Email)
| -- | The user asked to change the email address to the one already owned
ChangeEmailIdempotent

-------------------------------------------------------------------------------
-- Failures

Expand Down
51 changes: 0 additions & 51 deletions services/brig/src/Brig/API/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -520,57 +520,6 @@ checkRestrictedUserCreation new = do
)
$ throwE RegisterErrorUserCreationRestricted

-------------------------------------------------------------------------------
-- Change Email

-- | Call 'changeEmail' and process result: if email changes to itself, succeed, if not, send
-- validation email.
changeSelfEmail :: (Member BlockListStore r, Member UserKeyStore r, Member EmailSubsystem r) => UserId -> Email -> UpdateOriginType -> ExceptT HttpError (AppT r) ChangeEmailResponse
changeSelfEmail u email allowScim = do
changeEmail u email allowScim !>> Error.changeEmailError >>= \case
ChangeEmailIdempotent ->
pure ChangeEmailResponseIdempotent
ChangeEmailNeedsActivation (usr, adata, en) -> lift $ do
liftSem $ sendOutEmail usr adata en
wrapClient $ Data.updateEmailUnvalidated u email
wrapClient $ reindex u
pure ChangeEmailResponseNeedsActivation
where
sendOutEmail usr adata en = do
(maybe sendActivationMail (const sendEmailAddressUpdateMail) usr.userIdentity)
en
(userDisplayName usr)
(activationKey adata)
(activationCode adata)
(Just (userLocale usr))

-- | Prepare changing the email (checking a number of invariants).
changeEmail :: (Member BlockListStore r, Member UserKeyStore r) => UserId -> Email -> UpdateOriginType -> ExceptT ChangeEmailError (AppT r) ChangeEmailResult
changeEmail u email updateOrigin = do
em <-
either
(throwE . InvalidNewEmail email)
pure
(validateEmail email)
let ek = mkEmailKey em
blacklisted <- lift . liftSem $ BlockListStore.exists ek
when blacklisted $
throwE (ChangeBlacklistedEmail email)
available <- lift $ liftSem $ keyAvailable ek (Just u)
unless available $
throwE $
EmailExists email
usr <- maybe (throwM $ UserProfileNotFound u) pure =<< lift (wrapClient $ Data.lookupUser WithPendingInvitations u)
case emailIdentity =<< userIdentity usr of
-- The user already has an email address and the new one is exactly the same
Just current | current == em -> pure ChangeEmailIdempotent
_ -> do
unless (userManagedBy usr /= ManagedByScim || updateOrigin == UpdateOriginScim) $
throwE EmailManagedByScim
timeout <- setActivationTimeout <$> view settings
act <- lift . wrapClient $ Data.newActivation ek timeout (Just u)
pure $ ChangeEmailNeedsActivation (usr, act, em)

-------------------------------------------------------------------------------
-- Remove Email

Expand Down
9 changes: 0 additions & 9 deletions services/brig/src/Brig/Data/Activation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,6 @@ import Wire.PasswordResetCodeStore qualified as E
import Wire.PasswordResetCodeStore.Cassandra
import Wire.UserKeyStore

-- | The information associated with the pending activation of a 'UserKey'.
data Activation = Activation
{ -- | An opaque key for the original 'UserKey' pending activation.
activationKey :: !ActivationKey,
-- | The confidential activation code.
activationCode :: !ActivationCode
}
deriving (Eq, Show)

data ActivationError
= UserKeyExists !LT.Text
| InvalidActivationCodeWrongUser
Expand Down

0 comments on commit 9168390

Please sign in to comment.