-- 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 Galley.API.LegalHold.Get (getUserStatus) where

import Control.Lens (view)
import Data.ByteString.Conversion (toByteString')
import Data.Id
import Data.LegalHold (UserLegalHoldStatus (..))
import Data.Qualified
import Galley.API.Error
import Galley.Effects
import Galley.Effects.LegalHoldStore qualified as LegalHoldData
import Galley.Effects.TeamStore
import Imports
import Polysemy
import Polysemy.Error
import Polysemy.TinyLog qualified as P
import System.Logger.Class qualified as Log
import Wire.API.Error
import Wire.API.Error.Galley
import Wire.API.Team.LegalHold
import Wire.API.Team.LegalHold qualified as Public
import Wire.API.Team.Member
import Wire.API.User.Client.Prekey

-- | Learn whether a user has LH enabled and fetch pre-keys.
-- Note that this is accessible to ANY authenticated user, even ones outside the team
getUserStatus ::
  forall r.
  ( Member (Error InternalError) r,
    Member (ErrorS 'TeamMemberNotFound) r,
    Member LegalHoldStore r,
    Member TeamStore r,
    Member P.TinyLog r
  ) =>
  Local UserId ->
  TeamId ->
  UserId ->
  Sem r Public.UserLegalHoldStatusResponse
getUserStatus :: forall (r :: EffectRow).
(Member (Error InternalError) r,
 Member (ErrorS 'TeamMemberNotFound) r, Member LegalHoldStore r,
 Member TeamStore r, Member TinyLog r) =>
Local UserId
-> TeamId -> UserId -> Sem r UserLegalHoldStatusResponse
getUserStatus Local UserId
_lzusr TeamId
tid UserId
uid = do
  TeamMember' 'Required
teamMember <- forall {k} (e :: k) (r :: EffectRow) a.
Member (ErrorS e) r =>
Maybe a -> Sem r a
forall (e :: GalleyError) (r :: EffectRow) a.
Member (ErrorS e) r =>
Maybe a -> Sem r a
noteS @'TeamMemberNotFound (Maybe (TeamMember' 'Required) -> Sem r (TeamMember' 'Required))
-> Sem r (Maybe (TeamMember' 'Required))
-> Sem r (TeamMember' 'Required)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< TeamId -> UserId -> Sem r (Maybe (TeamMember' 'Required))
forall (r :: EffectRow).
Member TeamStore r =>
TeamId -> UserId -> Sem r (Maybe (TeamMember' 'Required))
getTeamMember TeamId
tid UserId
uid
  let status :: UserLegalHoldStatus
status = Getting
  UserLegalHoldStatus (TeamMember' 'Required) UserLegalHoldStatus
-> TeamMember' 'Required -> UserLegalHoldStatus
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting
  UserLegalHoldStatus (TeamMember' 'Required) UserLegalHoldStatus
forall (tag :: PermissionTag) (f :: * -> *).
Functor f =>
(UserLegalHoldStatus -> f UserLegalHoldStatus)
-> TeamMember' tag -> f (TeamMember' tag)
legalHoldStatus TeamMember' 'Required
teamMember
  (Maybe LastPrekey
mlk, Maybe ClientId
lcid) <- case UserLegalHoldStatus
status of
    UserLegalHoldStatus
UserLegalHoldNoConsent -> (Maybe LastPrekey, Maybe ClientId)
-> Sem r (Maybe LastPrekey, Maybe ClientId)
forall a. a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe LastPrekey
forall a. Maybe a
Nothing, Maybe ClientId
forall a. Maybe a
Nothing)
    UserLegalHoldStatus
UserLegalHoldDisabled -> (Maybe LastPrekey, Maybe ClientId)
-> Sem r (Maybe LastPrekey, Maybe ClientId)
forall a. a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe LastPrekey
forall a. Maybe a
Nothing, Maybe ClientId
forall a. Maybe a
Nothing)
    UserLegalHoldStatus
UserLegalHoldPending -> Sem r (Maybe LastPrekey, Maybe ClientId)
makeResponseDetails
    UserLegalHoldStatus
UserLegalHoldEnabled -> Sem r (Maybe LastPrekey, Maybe ClientId)
makeResponseDetails
  UserLegalHoldStatusResponse -> Sem r UserLegalHoldStatusResponse
forall a. a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (UserLegalHoldStatusResponse -> Sem r UserLegalHoldStatusResponse)
-> UserLegalHoldStatusResponse -> Sem r UserLegalHoldStatusResponse
forall a b. (a -> b) -> a -> b
$ UserLegalHoldStatus
-> Maybe LastPrekey
-> Maybe ClientId
-> UserLegalHoldStatusResponse
UserLegalHoldStatusResponse UserLegalHoldStatus
status Maybe LastPrekey
mlk Maybe ClientId
lcid
  where
    makeResponseDetails :: Sem r (Maybe LastPrekey, Maybe ClientId)
    makeResponseDetails :: Sem r (Maybe LastPrekey, Maybe ClientId)
makeResponseDetails = do
      Maybe LastPrekey
mLastKey <- (([Prekey], LastPrekey) -> LastPrekey)
-> Maybe ([Prekey], LastPrekey) -> Maybe LastPrekey
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Prekey], LastPrekey) -> LastPrekey
forall a b. (a, b) -> b
snd (Maybe ([Prekey], LastPrekey) -> Maybe LastPrekey)
-> Sem r (Maybe ([Prekey], LastPrekey)) -> Sem r (Maybe LastPrekey)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> UserId -> Sem r (Maybe ([Prekey], LastPrekey))
forall (r :: EffectRow).
Member LegalHoldStore r =>
UserId -> Sem r (Maybe ([Prekey], LastPrekey))
LegalHoldData.selectPendingPrekeys UserId
uid
      LastPrekey
lastKey <- case Maybe LastPrekey
mLastKey of
        Maybe LastPrekey
Nothing -> do
          (Msg -> Msg) -> Sem r ()
forall msg (r :: EffectRow).
Member (Logger msg) r =>
msg -> Sem r ()
P.err
            ((Msg -> Msg) -> Sem r ())
-> (ByteString -> Msg -> Msg) -> ByteString -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Msg -> Msg
forall a. ToBytes a => a -> Msg -> Msg
Log.msg
            (ByteString -> Sem r ()) -> ByteString -> Sem r ()
forall a b. (a -> b) -> a -> b
$ ByteString
"expected to find a prekey for user: "
              ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> UserId -> ByteString
forall a. ToByteString a => a -> ByteString
toByteString' UserId
uid
              ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
" but none was found"
          InternalError -> Sem r LastPrekey
forall e (r :: EffectRow) a. Member (Error e) r => e -> Sem r a
throw InternalError
NoPrekeyForUser
        Just LastPrekey
lstKey -> LastPrekey -> Sem r LastPrekey
forall a. a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure LastPrekey
lstKey
      let clientId :: ClientId
clientId = Prekey -> ClientId
clientIdFromPrekey (Prekey -> ClientId)
-> (LastPrekey -> Prekey) -> LastPrekey -> ClientId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LastPrekey -> Prekey
unpackLastPrekey (LastPrekey -> ClientId) -> LastPrekey -> ClientId
forall a b. (a -> b) -> a -> b
$ LastPrekey
lastKey
      (Maybe LastPrekey, Maybe ClientId)
-> Sem r (Maybe LastPrekey, Maybe ClientId)
forall a. a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (LastPrekey -> Maybe LastPrekey
forall a. a -> Maybe a
Just LastPrekey
lastKey, ClientId -> Maybe ClientId
forall a. a -> Maybe a
Just ClientId
clientId)