Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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/3-bug-fixes/fix-saml-corner-case
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
saml-auto-provisioned users don't get to change email address
54 changes: 32 additions & 22 deletions services/brig/src/Brig/API/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -607,32 +607,42 @@ changeSelfEmail u email allowScim = do
-- | Prepare changing the email (checking a number of invariants).
changeEmail :: UserId -> Email -> AllowSCIMUpdates -> ExceptT ChangeEmailError (AppT r) ChangeEmailResult
changeEmail u email allowScim = do
em <-
either
(throwE . InvalidNewEmail email)
pure
(validateEmail email)
let ek = userEmailKey em
blacklisted <- lift . wrapClient $ Blacklist.exists ek
eml <- either (throwE . InvalidNewEmail email) pure (validateEmail email)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please do refactorings in a separate PR :-P

let eky = userEmailKey eml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I personally find the name eky weird. But ok :-)

I would use eKey, emailKey or stick with ek


blacklisted <- lift . wrapClient $ Blacklist.exists eky
when blacklisted $
throwE (ChangeBlacklistedEmail email)
available <- lift . wrapClient $ Data.keyAvailable ek (Just u)

available <- lift . wrapClient $ Data.keyAvailable eky (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
|| allowScim == AllowSCIMUpdates
)
$ throwE EmailManagedByScim
throwE (EmailExists email)

usr <-
maybe (throwM $ UserProfileNotFound u) pure
=<< lift (wrapClient $ Data.lookupUser WithPendingInvitations u)

if (emailIdentity =<< userIdentity usr) == Just eml
then do
-- The user already has an email address and the new one is exactly the same
pure ChangeEmailIdempotent
else do
-- case 2: No or different old email address
let changeAllowed =
( userManagedBy usr /= ManagedByScim
|| allowScim == AllowSCIMUpdates -- spar is always allowed to call this function (from the scim handlers)
)
&& not
( -- user is auto-provisioned by saml (deprecated use case)
userManagedBy usr /= ManagedByScim && userHasSAML usr
)

unless changeAllowed $
throwE EmailManagedByScim

timeout <- setActivationTimeout <$> view settings
act <- lift . wrapClient $ Data.newActivation ek timeout (Just u)
pure $ ChangeEmailNeedsActivation (usr, act, em)
act <- lift . wrapClient $ Data.newActivation eky timeout (Just u)
pure $ ChangeEmailNeedsActivation (usr, act, eml)

-------------------------------------------------------------------------------
-- Change Phone
Expand Down
20 changes: 12 additions & 8 deletions services/brig/src/Brig/Data/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ module Brig.Data.User
reauthenticate,
filterActive,
isActivated,
isSamlUser,
userIdHasSAML,
userHasSAML,

-- * Lookups
lookupAccount,
Expand Down Expand Up @@ -212,17 +213,20 @@ reauthenticate u pw =
Just (Just pw', Ephemeral) -> maybeReAuth pw'
where
maybeReAuth pw' = case pw of
Nothing -> unlessM (isSamlUser u) $ throwE ReAuthMissingPassword
Nothing -> unlessM (userIdHasSAML u) $ throwE ReAuthMissingPassword
Just p ->
unless (verifyPassword p pw') $
throwE (ReAuthError AuthInvalidCredentials)

isSamlUser :: (MonadClient m, MonadReader Env m) => UserId -> m Bool
isSamlUser uid = do
account <- lookupAccount uid
case userIdentity . accountUser =<< account of
Just (SSOIdentity (UserSSOId _) _ _) -> pure True
_ -> pure False
userIdHasSAML :: (MonadClient m, MonadReader Env m) => UserId -> m Bool
userIdHasSAML uid = do
maybe False (userHasSAML . accountUser) <$> lookupAccount uid

userHasSAML :: User -> Bool
userHasSAML user = do
case userIdentity user of
Just (SSOIdentity (UserSSOId _) _ _) -> True
_ -> False

insertAccount ::
MonadClient m =>
Expand Down
2 changes: 1 addition & 1 deletion services/brig/src/Brig/User/Auth.hs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ revokeAccess ::
ExceptT AuthError m ()
revokeAccess u pw cc ll = do
lift $ Log.debug $ field "user" (toByteString u) . field "action" (Log.val "User.revokeAccess")
unlessM (Data.isSamlUser u) $ Data.authenticate u pw
unlessM (Data.userIdHasSAML u) $ Data.authenticate u pw
lift $ revokeCookies u cc ll

--------------------------------------------------------------------------------
Expand Down