diff --git a/changelog.d/5-internal/WPB-25033 b/changelog.d/5-internal/WPB-25033 new file mode 100644 index 00000000000..936805acd18 --- /dev/null +++ b/changelog.d/5-internal/WPB-25033 @@ -0,0 +1 @@ +Start migrating `ConversationSubsystem` errors to `ConversationSubsystemError` diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action.hs index 3c81ed700f9..5371b8fad4a 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action.hs @@ -118,6 +118,7 @@ import Wire.ConversationSubsystem.Action.Kick import Wire.ConversationSubsystem.Action.Leave import Wire.ConversationSubsystem.Action.Notify import Wire.ConversationSubsystem.Action.Reset +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Conversation import Wire.ConversationSubsystem.MLS.Migration import Wire.ConversationSubsystem.MLS.Removal @@ -696,9 +697,9 @@ instance IsConversationAction 'ConversationResetTag where Member E.MLSCommitLockStore r, Member Resource r, Member (Input ConversationSubsystemConfig) r, - Member (ErrorS MLSStaleMessage) r, Member (ErrorS ConvNotFound) r, Member (ErrorS InvalidOperation) r, + Member (Error ConversationSubsystemError) r, Member Random r, Member Now r, Member TinyLog r @@ -1277,7 +1278,7 @@ updateLocalConversationReset :: Member E.MLSCommitLockStore r, Member TeamSubsystem r, Member (Input ConversationSubsystemConfig) r, - Member (ErrorS MLSStaleMessage) r + Member (Error ConversationSubsystemError) r ) => Local ConvId -> Qualified UserId -> diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action/Reset.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action/Reset.hs index c31399a3b4d..79e7c8379d4 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action/Reset.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Action/Reset.hs @@ -46,6 +46,7 @@ import Wire.API.VersionInfo import Wire.BackendNotificationQueueAccess import Wire.ConversationStore import Wire.ConversationSubsystem.Action.Kick +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Util import Wire.ConversationSubsystem.Util import Wire.ExternalAccess @@ -58,9 +59,8 @@ import Wire.StoredConversation as Data resetLocalMLSMainConversation :: ( Member Now r, - Member (ErrorS MLSStaleMessage) r, Member (ErrorS ConvNotFound) r, - Member (ErrorS InvalidOperation) r, + Member (Error ConversationSubsystemError) r, Member BackendNotificationQueueAccess r, Member (FederationAPIAccess FederatorClient) r, Member ExternalAccess r, @@ -85,9 +85,9 @@ resetLocalMLSMainConversation qusr lcnv reset = do mlsData <- case cnv.protocol of ProtocolMLS md -> pure md ProtocolMixed md -> pure md - ProtocolProteus -> throwS @'InvalidOperation + ProtocolProteus -> throw ConversationSubsystemErrorInvalidOperation epoch <- case mlsData.cnvmlsActiveData of - Nothing -> throwS @'InvalidOperation + Nothing -> throw ConversationSubsystemErrorInvalidOperation Just ad -> pure ad.epoch let gid = mlsData.cnvmlsGroupId @@ -95,7 +95,7 @@ resetLocalMLSMainConversation qusr lcnv reset = do withCommitLock lcnvOrSub reset.groupId reset.epoch lift $ do unless (reset.groupId == gid) $ throwS @'ConvNotFound - unless (reset.epoch == epoch) $ throwS @'MLSStaleMessage + unless (reset.epoch == epoch) $ throw ConversationSubsystemErrorMLSStaleMessage removeAllMLSClients gid let newGid = case nextGenGroupId gid of diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Create.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Create.hs index 983fd7f0af2..cb92abe96f0 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Create.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Create.hs @@ -45,6 +45,7 @@ import Wire.BackendNotificationQueueAccess (BackendNotificationQueueAccess) import Wire.BrigAPIAccess import Wire.ConversationStore (ConversationStore) import Wire.ConversationSubsystem.CreateInternal +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.Util import Wire.FeaturesConfigSubsystem import Wire.FederationAPIAccess (FederationAPIAccess) @@ -69,12 +70,7 @@ createLegacyGroupConversation :: Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSNonEmptyMemberList) r, - Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r, - Member (ErrorS HistoryNotSupported) r, + Member (Error ConversationSubsystemError) r, Member (Input ConversationSubsystemConfig) r, Member LegalHoldStore r, Member TeamStore r, @@ -107,12 +103,7 @@ createGroupOwnConversation :: Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSNonEmptyMemberList) r, - Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r, - Member (ErrorS HistoryNotSupported) r, + Member (Error ConversationSubsystemError) r, Member (Input ConversationSubsystemConfig) r, Member LegalHoldStore r, Member TeamStore r, @@ -150,12 +141,7 @@ createGroupConversation :: Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSNonEmptyMemberList) r, - Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r, - Member (ErrorS HistoryNotSupported) r, + Member (Error ConversationSubsystemError) r, Member (Input ConversationSubsystemConfig) r, Member LegalHoldStore r, Member TeamStore r, @@ -209,9 +195,7 @@ createOne2OneConversation :: Member (Error InvalidInput) r, Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, - Member (ErrorS 'NonBindingTeam) r, - Member (ErrorS 'NoBindingTeamMembers) r, - Member (ErrorS 'TeamNotFound) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'InvalidOperation) r, Member (ErrorS 'NotConnected) r, Member TeamStore r, diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/CreateInternal.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/CreateInternal.hs index e28425c9061..b8e2b18d0b1 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/CreateInternal.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/CreateInternal.hs @@ -64,6 +64,7 @@ import Wire.BackendNotificationQueueAccess (BackendNotificationQueueAccess) import Wire.BrigAPIAccess import Wire.ConversationStore (ConversationStore) import Wire.ConversationStore qualified as ConvStore +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.One2One import Wire.ConversationSubsystem.Util import Wire.FeaturesConfigSubsystem @@ -93,12 +94,7 @@ createGroupConversationGeneric :: Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSNonEmptyMemberList) r, - Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r, - Member (ErrorS HistoryNotSupported) r, + Member (Error ConversationSubsystemError) r, Member (Input ConversationSubsystemConfig) r, Member LegalHoldStore r, Member TeamStore r, @@ -122,7 +118,7 @@ createGroupConversationGeneric lusr conn newConv = do checkCreateConvPermissions lusr newConv newConv.newConvTeam allUsers ensureNoLegalholdConflicts allUsers when (newConv.newConvHistory /= HistoryPrivate && newConv.newConvGroupConvType /= Channel) $ - throwS @HistoryNotSupported + throw ConversationSubsystemErrorHistoryNotSupported when (Public.newConvProtocol newConv == BaseProtocolMLSTag) $ do assertMLSEnabled @@ -141,9 +137,7 @@ createOne2OneConversationLogic :: Member (Error InvalidInput) r, Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, - Member (ErrorS 'NonBindingTeam) r, - Member (ErrorS 'NoBindingTeamMembers) r, - Member (ErrorS 'TeamNotFound) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'InvalidOperation) r, Member (ErrorS 'NotConnected) r, Member TeamStore r, @@ -288,7 +282,7 @@ createConnectConversationLogic lusr conn j = do | otherwise = pure conv ensureNoLegalholdConflicts :: - ( Member (ErrorS 'MissingLegalholdConsent) r, + ( Member (Error ConversationSubsystemError) r, Member (Input ConversationSubsystemConfig) r, Member LegalHoldStore r, Member TeamStore r, @@ -300,7 +294,7 @@ ensureNoLegalholdConflicts (UserList locals remotes) = do let FutureWork _remotes = FutureWork @'LegalholdPlusFederationNotImplemented remotes whenM (anyLegalholdActivated locals) $ unlessM (allLegalholdConsentGiven locals) $ - throwS @'MissingLegalholdConsent + throw ConversationSubsystemErrorMissingLegalholdConsent checkCreateConvPermissions :: ( Member BrigAPIAccess r, @@ -308,8 +302,7 @@ checkCreateConvPermissions :: Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r, + Member (Error ConversationSubsystemError) r, Member TeamStore r, Member FeaturesConfigSubsystem r, Member TeamCollaboratorsSubsystem r, @@ -356,22 +349,21 @@ checkCreateConvPermissions lusr newConv (Just tinfo) allUsers = do ( Member (ErrorS OperationDenied) r, Member FeaturesConfigSubsystem r, Member (ErrorS 'NotATeamMember) r, - Member (ErrorS 'ChannelsNotEnabled) r, - Member (ErrorS 'NotAnMlsConversation) r + Member (Error ConversationSubsystemError) r ) => TeamId -> Maybe TeamMember -> Sem r () ensureCreateChannelPermissions tid (Just tm) = do channelsConf :: LockableFeature ChannelsConfig <- getFeatureForTeam tid - when (channelsConf.status == FeatureStatusDisabled) $ throwS @'ChannelsNotEnabled - when (Public.newConvProtocol newConv /= BaseProtocolMLSTag) $ throwS @'NotAnMlsConversation + when (channelsConf.status == FeatureStatusDisabled) $ throw ConversationSubsystemErrorChannelsNotEnabled + when (Public.newConvProtocol newConv /= BaseProtocolMLSTag) $ throw ConversationSubsystemErrorNotAnMlsConversation case channelsConf.config.allowedToCreateChannels of Conf.Everyone -> pure () Conf.TeamMembers -> void $ permissionCheck AddRemoveConvMember $ Just tm Conf.Admins -> unless (isAdminOrOwner (tm ^. permissions)) $ throwS @OperationDenied ensureCreateChannelPermissions _ Nothing = do - throwS @'NotATeamMember + throw ConversationSubsystemErrorNotATeamMember getTeamMember :: (Member TeamStore r, Member TeamSubsystem r) => UserId -> Maybe TeamId -> Sem r (Maybe TeamMember) getTeamMember uid (Just tid) = TeamSubsystem.internalGetTeamMember uid tid @@ -490,7 +482,7 @@ createOne2OneConversationRemotely _ _ _ _name _mtid _ = throw FederationNotImplemented newRegularConversation :: - ( Member (ErrorS 'MLSNonEmptyMemberList) r, + ( Member (Error ConversationSubsystemError) r, Member (ErrorS OperationDenied) r, Member (Error InvalidInput) r, Member (Input ConversationSubsystemConfig) r, @@ -509,7 +501,7 @@ newRegularConversation lusr newConv = do users <- case Public.newConvProtocol newConv of BaseProtocolProteusTag -> checkedConvSize cfg uncheckedUsers BaseProtocolMLSTag -> do - unless (null uncheckedUsers) $ throwS @'MLSNonEmptyMemberList + unless (null uncheckedUsers) $ throw ConversationSubsystemErrorMLSNonEmptyMemberList pure mempty let usersWithoutCreator = (,newConvUsersRole newConv) <$> fromConvSize users newConvUsersRoles = @@ -586,10 +578,10 @@ ensureOne :: (Member (Error InvalidInput) r) => [a] -> Sem r a ensureOne [x] = pure x ensureOne _ = throw (InvalidRange "One-to-one conversations can only have a single invited member") -assertMLSEnabled :: (Member (Input ConversationSubsystemConfig) r, Member (ErrorS 'MLSNotEnabled) r) => Sem r () +assertMLSEnabled :: (Member (Input ConversationSubsystemConfig) r, Member (Error ConversationSubsystemError) r) => Sem r () assertMLSEnabled = do cfg <- input - when (null cfg.mlsKeys) $ throwS @'MLSNotEnabled + when (null cfg.mlsKeys) $ throw ConversationSubsystemErrorMLSNotEnabled newtype ConvSizeChecked f a = ConvSizeChecked {fromConvSize :: f a} deriving (Functor, Foldable, Traversable) @@ -623,11 +615,9 @@ throwErr :: (Member (Error InvalidInput) r) => String -> Sem r a throwErr = throw . InvalidRange . fromString checkBindingTeamPermissions :: - ( Member (ErrorS 'NoBindingTeamMembers) r, - Member (ErrorS 'NonBindingTeam) r, + ( Member (Error ConversationSubsystemError) r, Member (ErrorS 'NotATeamMember) r, Member (ErrorS OperationDenied) r, - Member (ErrorS 'TeamNotFound) r, Member TeamCollaboratorsSubsystem r, Member TeamStore r, Member TeamSubsystem r @@ -653,8 +643,8 @@ checkBindingTeamPermissions lusr lother tid = do unless (isJust mOtherTeamCollaborator) $ verifyMembership tid (tUnqualified lother) pure (Just tid) - Just _ -> throwS @'NonBindingTeam - Nothing -> throwS @'TeamNotFound + Just _ -> throw ConversationSubsystemErrorNonBindingTeam + Nothing -> throw ConversationSubsystemErrorTeamNotFound where guardPerm p m = if m `hasPermission` p @@ -662,7 +652,7 @@ checkBindingTeamPermissions lusr lother tid = do else throwS @OperationDenied verifyMembership :: - ( Member (ErrorS 'NoBindingTeamMembers) r, + ( Member (Error ConversationSubsystemError) r, Member TeamSubsystem r ) => TeamId -> @@ -671,7 +661,7 @@ verifyMembership :: verifyMembership tid u = do membership <- TeamSubsystem.internalGetTeamMember u tid when (isNothing membership) $ - throwS @'NoBindingTeamMembers + throw ConversationSubsystemErrorNoBindingTeamMembers sendCellsNotification :: ( Member NotificationSubsystem r, diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Errors.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Errors.hs index ec74e2b5267..7d98b05f2d5 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Errors.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Errors.hs @@ -38,7 +38,7 @@ import Wire.API.Routes.API (ServerEffect (interpretServerEffect)) data ConversationSubsystemError = ConversationSubsystemErrorConvAccessDenied | ConversationSubsystemErrorNotATeamMember - | ConversationSubsystemErrorperationDenied + | ConversationSubsystemErrorOperationDenied | ConversationSubsystemErrorNotConnected | ConversationSubsystemErrorMLSNotEnabled | ConversationSubsystemErrorMLSNonEmptyMemberList @@ -109,7 +109,7 @@ instance APIError ConversationSubsystemError where \case ConversationSubsystemErrorConvAccessDenied -> toResponse $ Tagged @'ConvAccessDenied () ConversationSubsystemErrorNotATeamMember -> toResponse $ Tagged @'NotATeamMember () - ConversationSubsystemErrorperationDenied -> toResponse $ Tagged @OperationDenied () + ConversationSubsystemErrorOperationDenied -> toResponse $ Tagged @OperationDenied () ConversationSubsystemErrorNotConnected -> toResponse $ Tagged @'NotConnected () ConversationSubsystemErrorMLSNotEnabled -> toResponse $ Tagged @'MLSNotEnabled () ConversationSubsystemErrorMLSNonEmptyMemberList -> toResponse $ Tagged @'MLSNonEmptyMemberList () @@ -181,48 +181,24 @@ type ConversationSubsystemErrorEffects = ErrorS OperationDenied, ErrorS 'NotConnected, ErrorS 'MLSNotEnabled, - ErrorS 'MLSNonEmptyMemberList, ErrorS 'MissingLegalholdConsent, ErrorS 'NonBindingTeam, - ErrorS 'NoBindingTeamMembers, ErrorS 'TeamNotFound, ErrorS 'InvalidOperation, ErrorS 'ConvNotFound, - ErrorS 'ChannelsNotEnabled, - ErrorS 'NotAnMlsConversation, - ErrorS 'MLSLegalholdIncompatible, - ErrorS 'MLSIdentityMismatch, ErrorS 'MLSUnsupportedMessage, ErrorS 'MLSStaleMessage, - ErrorS 'MLSProposalNotFound, - ErrorS 'MLSCommitMissingReferences, - ErrorS 'MLSSelfRemovalNotAllowed, - ErrorS 'MLSClientSenderUserMismatch, - ErrorS 'MLSSubConvClientNotInParent, - ErrorS 'MLSInvalidLeafNodeSignature, - ErrorS 'MLSClientMismatch, - ErrorS 'MLSInvalidLeafNodeIndex, - ErrorS 'MLSUnsupportedProposal, ErrorS 'GroupIdVersionNotSupported, ErrorS 'ConvMemberNotFound, ErrorS 'HistoryNotSupported, - ErrorS MLSGroupConversationMismatch, ErrorS ('ActionDenied ConvRole.LeaveConversation), ErrorS ('ActionDenied ConvRole.RemoveConversationMember), ErrorS ('ActionDenied ConvRole.DeleteConversation), - ErrorS 'BroadcastLimitExceeded, - ErrorS 'MLSFederatedResetNotSupported, ErrorS 'MLSSubConvUnsupportedConvType, - ErrorS 'TeamMemberNotFound, - ErrorS 'AccessDenied, ErrorS 'MLSMissingGroupInfo, ErrorS 'CodeNotFound, - ErrorS 'InvalidConversationPassword, ErrorS 'GuestLinksDisabled, - ErrorS 'MLSFederatedOne2OneNotSupported, ErrorS 'TooManyMembers, - ErrorS 'CreateConversationCodeConflict, - ErrorS 'InvalidTarget, ErrorS 'MLSReadReceiptsNotAllowed, ErrorS 'InvalidTargetAccess, ErrorS 'ConvInvalidProtocolTransition, @@ -238,7 +214,6 @@ type ConversationSubsystemErrorEffects = Error UnreachableBackends, Error InternalError, Error InvalidInput, - Error AuthenticationError, Error MLSProtocolError, Error GroupInfoDiagnostics, Error MLSOutOfSyncError, @@ -249,8 +224,7 @@ type ConversationSubsystemErrorEffects = mapErrors :: ( Member (Error ConversationSubsystemError) r, - Member (Error JSONResponse) r, - Member (Error DynError) r + Member (Error JSONResponse) r ) => InterpretersFor ConversationSubsystemErrorEffects r mapErrors = @@ -260,7 +234,6 @@ mapErrors = . mapError (ConversationSubsystemErrorMLSOutOfSyncError) . mapError (ConversationSubsystemErrorGroupInfoDiagnostics) . mapError (ConversationSubsystemErrorMLSProtocolError) - . interpretServerEffect . mapError (ConversationSubsystemErrorInvalidInput) . mapError (ConversationSubsystemErrorInternalError) . mapError (ConversationSubsystemErrorUnreachableBackends) @@ -276,50 +249,26 @@ mapErrors = . mapError (const ConversationSubsystemErrorConvInvalidProtocolTransition) . mapError (const ConversationSubsystemErrorInvalidTargetAccess) . mapError (const ConversationSubsystemErrorMLSReadReceiptsNotAllowed) - . mapError (const ConversationSubsystemErrorInvalidTarget) - . mapError (const ConversationSubsystemErrorCreateConversationCodeConflict) . mapError (const ConversationSubsystemErrorTooManyMembers) - . mapError (const ConversationSubsystemErrorMLSFederatedOne2OneNotSupported) . mapError (const ConversationSubsystemErrorGuestLinksDisabled) - . mapError (const ConversationSubsystemErrorInvalidConversationPassword) . mapError (const ConversationSubsystemErrorCodeNotFound) . mapError (const ConversationSubsystemErrorMLSMissingGroupInfo) - . mapError (const ConversationSubsystemErrorAccessDenied) - . mapError (const ConversationSubsystemErrorTeamMemberNotFound) . mapError (const ConversationSubsystemErrorMLSSubConvUnsupportedConvType) - . mapError (const ConversationSubsystemErrorMLSFederatedResetNotSupported) - . mapError (const ConversationSubsystemErrorBroadcastLimitExceeded) . mapError (const ConversationSubsystemErrorActionDeniedDeleteConversation) . mapError (const ConversationSubsystemErrorActionDeniedRemoveConversationMember) . mapError (const ConversationSubsystemErrorActionDeniedLeaveConversation) - . mapError (const ConversationSubsystemErrorLSGroupConversationMismatch) . mapError (const ConversationSubsystemErrorHistoryNotSupported) . mapError (const ConversationSubsystemErrorConvMemberNotFound) . mapError (const ConversationSubsystemErrorGroupIdVersionNotSupported) - . mapError (const ConversationSubsystemErrorMLSUnsupportedProposal) - . mapError (const ConversationSubsystemErrorMLSInvalidLeafNodeIndex) - . mapError (const ConversationSubsystemErrorMLSClientMismatch) - . mapError (const ConversationSubsystemErrorMLSInvalidLeafNodeSignature) - . mapError (const ConversationSubsystemErrorMLSSubConvClientNotInParent) - . mapError (const ConversationSubsystemErrorMLSClientSenderUserMismatch) - . mapError (const ConversationSubsystemErrorMLSSelfRemovalNotAllowed) - . mapError (const ConversationSubsystemErrorMLSCommitMissingReferences) - . mapError (const ConversationSubsystemErrorMLSProposalNotFound) . mapError (const ConversationSubsystemErrorMLSStaleMessage) . mapError (const ConversationSubsystemErrorMLSUnsupportedMessage) - . mapError (const ConversationSubsystemErrorMLSIdentityMismatch) - . mapError (const ConversationSubsystemErrorMLSLegalholdIncompatible) - . mapError (const ConversationSubsystemErrorNotAnMlsConversation) - . mapError (const ConversationSubsystemErrorChannelsNotEnabled) . mapError (const ConversationSubsystemErrorConvNotFound) . mapError (const ConversationSubsystemErrorInvalidOperation) . mapError (const ConversationSubsystemErrorTeamNotFound) - . mapError (const ConversationSubsystemErrorNoBindingTeamMembers) . mapError (const ConversationSubsystemErrorNonBindingTeam) . mapError (const ConversationSubsystemErrorMissingLegalholdConsent) - . mapError (const ConversationSubsystemErrorMLSNonEmptyMemberList) . mapError (const ConversationSubsystemErrorMLSNotEnabled) . mapError (const ConversationSubsystemErrorNotConnected) - . mapError (const ConversationSubsystemErrorperationDenied) + . mapError (const ConversationSubsystemErrorOperationDenied) . mapError (const ConversationSubsystemErrorNotATeamMember) . mapError (const ConversationSubsystemErrorConvAccessDenied) diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Federation.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Federation.hs index 34a22f5dd4f..5af84fc4c88 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Federation.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Federation.hs @@ -76,6 +76,7 @@ import Wire.BrigAPIAccess (BrigAPIAccess) import Wire.CodeStore import Wire.ConversationStore qualified as E import Wire.ConversationSubsystem.Action +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Enabled import Wire.ConversationSubsystem.MLS.GroupInfo import Wire.ConversationSubsystem.MLS.GroupInfoCheck (GroupInfoCheckEnabled) @@ -341,6 +342,7 @@ sendMessage :: Member (Input FeatureFlags) r, Member E.ConversationStore r, Member (Error InvalidInput) r, + Member (Error ConversationSubsystemError) r, Member (FederationAPIAccess FederatorClient) r, Member BackendNotificationQueueAccess r, Member NotificationSubsystem r, @@ -522,6 +524,7 @@ updateConversation origDomain updateRequest = do mkResponse = fmap (either ConversationUpdateResponseError Imports.id) . runError @GalleyError + . mapError conversationSubsystemErrorToGalleyError . fmap (fromRight ConversationUpdateResponseNoChanges) . runError @NoChanges . fmap (either ConversationUpdateResponseNonFederatingBackends Imports.id) @@ -580,9 +583,6 @@ sendMLSCommitBundle :: Member ExternalAccess r, Member (Error FederationError) r, Member (Error InternalError) r, - Member (ErrorS 'MLSClientMismatch) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSUnsupportedProposal) r, Member (FederationAPIAccess FederatorClient) r, Member NotificationSubsystem r, Member (Input (Local ())) r, @@ -605,7 +605,7 @@ sendMLSCommitBundle :: Domain -> MLSMessageSendRequest -> Sem r MLSMessageResponse -sendMLSCommitBundle remoteDomain msr = handleMLSMessageErrors $ do +sendMLSCommitBundle remoteDomain msr = handleMLSMessageErrors . mapError conversationSubsystemErrorToGalleyError $ do assertMLSEnabled loc <- qualifyLocal () let sender = toRemoteUnsafe remoteDomain msr.sender @@ -613,9 +613,9 @@ sendMLSCommitBundle remoteDomain msr = handleMLSMessageErrors $ do either (throw . mlsProtocolError) pure $ decodeMLS' (fromBase64ByteString msr.rawMessage) - ibundle <- noteS @'MLSUnsupportedMessage $ mkIncomingBundle bundle + ibundle <- note ConversationSubsystemErrorMLSUnsupportedMessage $ mkIncomingBundle bundle (ctype, qConvOrSub) <- getConvFromGroupId ibundle.groupId - when (qUnqualified qConvOrSub /= msr.convOrSubId) $ throwS @'MLSGroupConversationMismatch + when (qUnqualified qConvOrSub /= msr.convOrSubId) $ throw ConversationSubsystemErrorLSGroupConversationMismatch -- this cannot throw the error since we always pass the sender which is qualified to be remote MLSMessageResponseUpdates @@ -646,9 +646,6 @@ sendMLSMessage :: Member (Error FederationError) r, Member (Error InternalError) r, Member (FederationAPIAccess FederatorClient) r, - Member (ErrorS 'MLSClientMismatch) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSUnsupportedProposal) r, Member NotificationSubsystem r, Member (Input (Local ())) r, Member (Input (Maybe (MLSKeysByPurpose MLSPrivateKeys))) r, @@ -662,14 +659,14 @@ sendMLSMessage :: Domain -> MLSMessageSendRequest -> Sem r MLSMessageResponse -sendMLSMessage remoteDomain msr = handleMLSMessageErrors $ do +sendMLSMessage remoteDomain msr = handleMLSMessageErrors . mapError conversationSubsystemErrorToGalleyError $ do assertMLSEnabled loc <- qualifyLocal () let sender = toRemoteUnsafe remoteDomain msr.sender raw <- either (throw . mlsProtocolError) pure $ decodeMLS' (fromBase64ByteString msr.rawMessage) - msg <- noteS @'MLSUnsupportedMessage $ mkIncomingMessage raw + msg <- note ConversationSubsystemErrorMLSUnsupportedMessage $ mkIncomingMessage raw (ctype, qConvOrSub) <- getConvFromGroupId msg.groupId - when (qUnqualified qConvOrSub /= msr.convOrSubId) $ throwS @'MLSGroupConversationMismatch + when (qUnqualified qConvOrSub /= msr.convOrSubId) $ throw ConversationSubsystemErrorLSGroupConversationMismatch MLSMessageResponseUpdates . map lcuUpdate <$> postMLSMessage loc @@ -692,6 +689,7 @@ getSubConversationForRemoteUser :: getSubConversationForRemoteUser domain GetSubConversationsRequest {..} = fmap (either GetSubConversationsResponseError GetSubConversationsResponseSuccess) . runError @GalleyError + . mapError conversationSubsystemErrorToGalleyError . mapToGalleyError @MLSGetSubConvStaticErrors $ do let qusr = Qualified gsreqUser domain @@ -718,6 +716,7 @@ leaveSubConversation domain lscr = do . runError @MLSProtocolError . fmap (either LeaveSubConversationResponseError Imports.id) . runError @GalleyError + . mapError conversationSubsystemErrorToGalleyError . mapToGalleyError @LeaveSubConversationStaticErrors $ leaveLocalSubConversation cid lcnv (lscrSubConv lscr) $> LeaveSubConversationResponseOk @@ -739,6 +738,7 @@ deleteSubConversationForRemoteUser domain DeleteSubConversationFedRequest {..} = (\() -> DeleteSubConversationResponseSuccess) ) . runError @GalleyError + . mapError conversationSubsystemErrorToGalleyError . mapToGalleyError @MLSDeleteSubConvStaticErrors $ do let qusr = Qualified dscreqUser domain @@ -807,6 +807,78 @@ getOne2OneConversation domain (GetOne2OneConversationRequest self other) = -------------------------------------------------------------------------------- -- Error handling machinery +-- | Convert ConversationSubsystemError to GalleyError for federation protocol +conversationSubsystemErrorToGalleyError :: ConversationSubsystemError -> GalleyError +conversationSubsystemErrorToGalleyError = \case + ConversationSubsystemErrorConvAccessDenied -> ConvAccessDenied + ConversationSubsystemErrorNotATeamMember -> NotATeamMember + ConversationSubsystemErrorOperationDenied -> InvalidOperation + ConversationSubsystemErrorNotConnected -> NotConnected + ConversationSubsystemErrorMLSNotEnabled -> MLSNotEnabled + ConversationSubsystemErrorMLSNonEmptyMemberList -> MLSNonEmptyMemberList + ConversationSubsystemErrorMissingLegalholdConsent -> MissingLegalholdConsent + ConversationSubsystemErrorNonBindingTeam -> NonBindingTeam + ConversationSubsystemErrorNoBindingTeamMembers -> NoBindingTeamMembers + ConversationSubsystemErrorTeamNotFound -> TeamNotFound + ConversationSubsystemErrorInvalidOperation -> InvalidOperation + ConversationSubsystemErrorConvNotFound -> ConvNotFound + ConversationSubsystemErrorChannelsNotEnabled -> ChannelsNotEnabled + ConversationSubsystemErrorNotAnMlsConversation -> NotAnMlsConversation + ConversationSubsystemErrorMLSLegalholdIncompatible -> MLSLegalholdIncompatible + ConversationSubsystemErrorMLSIdentityMismatch -> MLSIdentityMismatch + ConversationSubsystemErrorMLSUnsupportedMessage -> MLSUnsupportedMessage + ConversationSubsystemErrorMLSStaleMessage -> MLSStaleMessage + ConversationSubsystemErrorMLSProposalNotFound -> MLSProposalNotFound + ConversationSubsystemErrorMLSCommitMissingReferences -> MLSCommitMissingReferences + ConversationSubsystemErrorMLSSelfRemovalNotAllowed -> MLSSelfRemovalNotAllowed + ConversationSubsystemErrorMLSClientSenderUserMismatch -> MLSClientSenderUserMismatch + ConversationSubsystemErrorMLSSubConvClientNotInParent -> MLSSubConvClientNotInParent + ConversationSubsystemErrorMLSInvalidLeafNodeSignature -> MLSInvalidLeafNodeSignature + ConversationSubsystemErrorMLSClientMismatch -> MLSClientMismatch + ConversationSubsystemErrorMLSInvalidLeafNodeIndex -> MLSInvalidLeafNodeIndex + ConversationSubsystemErrorMLSUnsupportedProposal -> MLSUnsupportedProposal + ConversationSubsystemErrorGroupIdVersionNotSupported -> GroupIdVersionNotSupported + ConversationSubsystemErrorConvMemberNotFound -> ConvMemberNotFound + ConversationSubsystemErrorHistoryNotSupported -> HistoryNotSupported + ConversationSubsystemErrorLSGroupConversationMismatch -> MLSGroupConversationMismatch + ConversationSubsystemErrorActionDeniedLeaveConversation -> ActionDenied LeaveConversation + ConversationSubsystemErrorActionDeniedRemoveConversationMember -> ActionDenied RemoveConversationMember + ConversationSubsystemErrorActionDeniedDeleteConversation -> ActionDenied DeleteConversation + ConversationSubsystemErrorBroadcastLimitExceeded -> BroadcastLimitExceeded + ConversationSubsystemErrorMLSFederatedResetNotSupported -> MLSFederatedResetNotSupported + ConversationSubsystemErrorMLSSubConvUnsupportedConvType -> MLSSubConvUnsupportedConvType + ConversationSubsystemErrorTeamMemberNotFound -> TeamMemberNotFound + ConversationSubsystemErrorAccessDenied -> AccessDenied + ConversationSubsystemErrorMLSMissingGroupInfo -> MLSMissingGroupInfo + ConversationSubsystemErrorCodeNotFound -> CodeNotFound + ConversationSubsystemErrorInvalidConversationPassword -> InvalidConversationPassword + ConversationSubsystemErrorGuestLinksDisabled -> GuestLinksDisabled + ConversationSubsystemErrorMLSFederatedOne2OneNotSupported -> MLSFederatedOne2OneNotSupported + ConversationSubsystemErrorTooManyMembers -> TooManyMembers + ConversationSubsystemErrorCreateConversationCodeConflict -> CreateConversationCodeConflict + ConversationSubsystemErrorInvalidTarget -> InvalidTarget + ConversationSubsystemErrorMLSReadReceiptsNotAllowed -> MLSReadReceiptsNotAllowed + ConversationSubsystemErrorInvalidTargetAccess -> InvalidTargetAccess + ConversationSubsystemErrorConvInvalidProtocolTransition -> ConvInvalidProtocolTransition + ConversationSubsystemErrorMLSMigrationCriteriaNotSatisfied -> MLSMigrationCriteriaNotSatisfied + ConversationSubsystemErrorActionDeniedAddConversationMember -> ActionDenied AddConversationMember + ConversationSubsystemErrorActionDeniedModifyOtherConversationMember -> ActionDenied ModifyOtherConversationMember + ConversationSubsystemErrorActionDeniedModifyConversationName -> ActionDenied ModifyConversationName + ConversationSubsystemErrorActionDeniedModifyConversationMessageTimer -> ActionDenied ModifyConversationMessageTimer + ConversationSubsystemErrorActionDeniedModifyConversationReceiptMode -> ActionDenied ModifyConversationReceiptMode + ConversationSubsystemErrorActionDeniedModifyConversationAccess -> ActionDenied ModifyConversationAccess + ConversationSubsystemErrorActionDeniedModifyAddPermission -> ActionDenied ModifyAddPermission + -- Fallback for errors that don't have direct GalleyError equivalents + ConversationSubsystemErrorFederationError _ -> MLSProtocolErrorTag + ConversationSubsystemErrorUnreachableBackends _ -> MLSProtocolErrorTag + ConversationSubsystemErrorInternalError _ -> MLSProtocolErrorTag + ConversationSubsystemErrorInvalidInput _ -> MLSProtocolErrorTag + ConversationSubsystemErrorMLSProtocolError _ -> MLSProtocolErrorTag + ConversationSubsystemErrorGroupInfoDiagnostics _ -> MLSProtocolErrorTag + ConversationSubsystemErrorMLSOutOfSyncError _ -> MLSProtocolErrorTag + ConversationSubsystemErrorNonFederatingBackends _ -> MLSProtocolErrorTag + ConversationSubsystemErrorUnreachableBackendsLegacy _ -> MLSProtocolErrorTag + class ToGalleyRuntimeError (effs :: EffectRow) r where mapToGalleyError :: (Member (Error GalleyError) r) => @@ -919,6 +991,7 @@ queryGroupInfo :: queryGroupInfo origDomain req = fmap (either GetGroupInfoResponseError GetGroupInfoResponseState) . runError @GalleyError + . mapError conversationSubsystemErrorToGalleyError . mapToGalleyError @MLSGroupInfoStaticErrors $ do assertMLSEnabled diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Interpreter.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Interpreter.hs index 672372cf8bc..6f2b783e168 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Interpreter.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Interpreter.hs @@ -34,7 +34,6 @@ import Polysemy.Input import Polysemy.Resource (Resource) import Polysemy.TinyLog (TinyLog) import Wire.API.Conversation.Config -import Wire.API.Error import Wire.API.Federation.Client (FederatorClient) import Wire.API.MLS.Keys (MLSKeysByPurpose, MLSPrivateKeys) import Wire.API.Team.FeatureFlags (FanoutLimit, FeatureFlags) @@ -86,7 +85,6 @@ import Wire.UserGroupStore (UserGroupStore) interpretConversationSubsystem :: ( Member (Error ConversationSubsystemError) r, Member (Error JSONResponse) r, - Member (Error DynError) r, Member UserGroupStore r, Member (Input (Maybe GuestLinkTTLSeconds)) r, Member HashPassword r, diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/CheckClients.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/CheckClients.hs index 388d6a16422..a16dedc92f6 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/CheckClients.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/CheckClients.hs @@ -32,7 +32,6 @@ import Data.Tuple.Extra import Imports import Polysemy import Polysemy.Error -import Wire.API.Error import Wire.API.Error.Galley import Wire.API.Federation.Client (FederatorClient) import Wire.API.Federation.Error @@ -42,14 +41,14 @@ import Wire.API.MLS.LeafNode import Wire.API.User.Client import Wire.BrigAPIAccess (BrigAPIAccess) import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Commit.Core import Wire.FederationAPIAccess (FederationAPIAccess) checkClients :: ( Member BrigAPIAccess r, Member (FederationAPIAccess FederatorClient) r, - Member (ErrorS MLSClientMismatch) r, - Member (ErrorS MLSIdentityMismatch) r, + Member (Error ConversationSubsystemError) r, Member (Error MLSProtocolError) r ) => Local ConvOrSubConv -> @@ -98,7 +97,7 @@ checkClients lConvOrSub ciphersuite newCM = do ) $ -- FUTUREWORK: turn this error into a proper response - throwS @'MLSClientMismatch + throw ConversationSubsystemErrorMLSClientMismatch pure False diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/Core.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/Core.hs index a51f0adcbcd..ce15c1d1183 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/Core.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/Core.hs @@ -63,6 +63,7 @@ import Wire.BackendNotificationQueueAccess import Wire.BrigAPIAccess import Wire.ConversationStore import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Conversation import Wire.ConversationSubsystem.MLS.IncomingMessage import Wire.ConversationSubsystem.MLS.Proposal @@ -81,15 +82,13 @@ type HasProposalActionEffects r = Member BrigAPIAccess r, Member ConversationStore r, Member (Error InternalError) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSClientMismatch) r, Member (Error MLSProposalFailure) r, Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'MLSUnsupportedProposal) r, Member (Error MLSProtocolError) r, Member (Error NonFederatingBackends) r, Member (Error UnreachableBackends) r, - Member (ErrorS 'MLSSelfRemovalNotAllowed) r, Member (ErrorS 'GroupIdVersionNotSupported) r, Member ExternalAccess r, Member (FederationAPIAccess FederatorClient) r, @@ -105,10 +104,7 @@ type HasProposalActionEffects r = ) getCommitData :: - ( HasProposalEffects r, - Member (ErrorS 'MLSProposalNotFound) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r - ) => + (HasProposalEffects r) => SenderIdentity -> Local ConvOrSubConv -> Epoch -> @@ -225,7 +221,7 @@ getRemoteMLSClient rusr cid suite = do <|> fmap extractClient (fedClient @'Brig @(Versioned 'V0 "get-mls-clients") (mlsClientsRequestToV0 mcr)) checkSignatureKey :: - (Member (ErrorS MLSIdentityMismatch) r) => + (Member (Error ConversationSubsystemError) r) => Maybe LeafNode -> Maybe ByteString -> Sem r () @@ -241,16 +237,15 @@ checkSignatureKey mLeaf mKey = do key -> key /= Just leaf.signatureKey Nothing -> False ) - $ throwS @'MLSIdentityMismatch + $ throw ConversationSubsystemErrorMLSIdentityMismatch -- | Check that the leaf node in an update path, if present, has the correct signature key. checkUpdatePath :: - ( Member (ErrorS MLSIdentityMismatch) r, - Member (Error MLSProtocolError) r, + ( Member (Error MLSProtocolError) r, Member (Error FederationError) r, Member BrigAPIAccess r, Member (FederationAPIAccess FederatorClient) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r + Member (Error ConversationSubsystemError) r ) => Local ConvOrSubConv -> SenderIdentity -> @@ -261,7 +256,7 @@ checkUpdatePath lConvOrSub senderIdentity ciphersuite path = for_ senderIdentity let groupId = cnvmlsGroupId (tUnqualified lConvOrSub).mlsMeta let extra = LeafNodeTBSExtraCommit groupId index case validateLeafNode ciphersuite (Just senderIdentity.client) extra path.leaf.value of - Left InvalidLeafNodeSignature -> throwS @'MLSInvalidLeafNodeSignature + Left InvalidLeafNodeSignature -> throw ConversationSubsystemErrorMLSInvalidLeafNodeSignature Left errMsg -> throw $ mlsProtocolError ("Tried to add invalid LeafNode: " <> toText errMsg) diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/ExternalCommit.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/ExternalCommit.hs index 10fa4e6bd58..f3c609bd1fb 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/ExternalCommit.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/ExternalCommit.hs @@ -34,7 +34,6 @@ import Polysemy.Error import Polysemy.Resource (Resource) import Polysemy.State import Wire.API.Conversation.Protocol -import Wire.API.Error import Wire.API.Error.Galley import Wire.API.Federation.Error import Wire.API.MLS.CipherSuite @@ -46,6 +45,7 @@ import Wire.API.MLS.ProposalTag import Wire.API.MLS.SubConversation import Wire.ConversationStore import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Commit.Core import Wire.ConversationSubsystem.MLS.IncomingMessage import Wire.ConversationSubsystem.MLS.Proposal @@ -60,10 +60,7 @@ data ExternalCommitAction = ExternalCommitAction getExternalCommitData :: forall r. ( Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSStaleMessage) r, - Member (ErrorS 'MLSUnsupportedProposal) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r + Member (Error ConversationSubsystemError) r ) => ClientIdentity -> Local ConvOrSubConv -> @@ -76,7 +73,7 @@ getExternalCommitData senderIdentity lConvOrSub epoch commit = do note (mlsProtocolError "The first commit in a group cannot be external") $ cnvmlsActiveData convOrSub.mlsMeta let curEpoch = activeData.epoch - when (epoch /= curEpoch) $ throwS @'MLSStaleMessage + when (epoch /= curEpoch) $ throw ConversationSubsystemErrorMLSStaleMessage when (epoch == Epoch 0) $ throw $ mlsProtocolError "The first commit in a group cannot be external" @@ -133,12 +130,8 @@ getExternalCommitData senderIdentity lConvOrSub epoch commit = do processExternalCommit :: forall r. ( Member (Error FederationError) r, - Member (ErrorS MLSStaleMessage) r, - Member (ErrorS MLSIdentityMismatch) r, - Member (ErrorS MLSSubConvClientNotInParent) r, Member Resource r, HasProposalActionEffects r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, Member MLSCommitLockStore r ) => SenderIdentity -> @@ -154,10 +147,10 @@ processExternalCommit senderIdentity lConvOrSub ciphersuite ciphersuiteUpdate ep groupId = cnvmlsGroupId convOrSub.mlsMeta -- only members can join a subconversation - forOf_ _SubConv convOrSub $ \(mlsConv, _) -> + forOf_ _SubConv convOrSub \(mlsConv, _) -> unless (isClientMember senderIdentity.client (mcMembers mlsConv)) $ lift $ - throwS @'MLSSubConvClientNotInParent + throw ConversationSubsystemErrorMLSSubConvClientNotInParent -- extract update path updatePath <- diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/InternalCommit.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/InternalCommit.hs index 8abc46eefb0..609e5a990e5 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/InternalCommit.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Commit/InternalCommit.hs @@ -53,6 +53,7 @@ import Wire.API.Unreachable import Wire.ConversationStore import Wire.ConversationStore.MLS.Types import Wire.ConversationSubsystem.Action +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.CheckClients import Wire.ConversationSubsystem.MLS.Commit.Core import Wire.ConversationSubsystem.MLS.Conversation @@ -71,15 +72,10 @@ processInternalCommit :: forall r. ( HasProposalEffects r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSCommitMissingReferences) r, - Member (ErrorS 'MLSSelfRemovalNotAllowed) r, - Member (ErrorS 'MLSStaleMessage) r, - Member (ErrorS 'MLSIdentityMismatch) r, Member (ErrorS 'MissingLegalholdConsent) r, Member (ErrorS 'GroupIdVersionNotSupported) r, Member Resource r, Member Random r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, Member MLSCommitLockStore r, Member FederationSubsystem r, Member TeamSubsystem r, @@ -139,12 +135,12 @@ processInternalCommit senderIdentity con lConvOrSub ciphersuite ciphersuiteUpdat -- return error if the user is trying to remove themself when (cidQualifiedUser senderIdentity.client == qtarget) $ - throwS @'MLSSelfRemovalNotAllowed + throw ConversationSubsystemErrorMLSSelfRemovalNotAllowed -- FUTUREWORK: add tests against this situation for conv v subconv when (removedClients /= clientsInConv) $ do -- FUTUREWORK: turn this error into a proper response - throwS @'MLSClientMismatch + throw ConversationSubsystemErrorMLSClientMismatch pure qtarget @@ -323,7 +319,7 @@ existingMembers lconv = existingLocalMembers lconv <> existingRemoteMembers lcon checkReferences :: ( Member ProposalStore r, - Member (ErrorS MLSCommitMissingReferences) r + Member (Error ConversationSubsystemError) r ) => ConvOrSubConv -> Epoch -> Commit -> Sem r () checkReferences convOrSub epoch commit = do @@ -345,4 +341,4 @@ checkReferences convOrSub epoch commit = do for_ missingProposals $ \prop -> do case getDeletedIndex prop of Just i | Set.member i deletedIndices -> pure () - _ -> throwS @'MLSCommitMissingReferences + _ -> throw ConversationSubsystemErrorMLSCommitMissingReferences diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfo.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfo.hs index f52928307d2..a6ba0359c4d 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfo.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfo.hs @@ -26,6 +26,7 @@ where import Data.Id as Id import Data.Json.Util import Data.Qualified +import Data.Text qualified as T import Imports import Polysemy import Polysemy.Error @@ -40,6 +41,7 @@ import Wire.API.MLS.GroupInfo import Wire.API.MLS.Keys (MLSKeysByPurpose, MLSPrivateKeys) import Wire.API.MLS.SubConversation import Wire.ConversationStore qualified as E +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError) import Wire.ConversationSubsystem.MLS.Enabled import Wire.ConversationSubsystem.MLS.Util import Wire.ConversationSubsystem.Util @@ -52,7 +54,8 @@ type MLSGroupInfoStaticErrors = ] getGroupInfo :: - ( Member (Input (Maybe (MLSKeysByPurpose MLSPrivateKeys))) r, + ( Member (Error ConversationSubsystemError) r, + Member (Input (Maybe (MLSKeysByPurpose MLSPrivateKeys))) r, Member E.ConversationStore r, Member (Error FederationError) r, Member (E.FederationAPIAccess FederatorClient) r, @@ -81,10 +84,10 @@ getGroupInfoFromLocalConv qusr lcnvId = do >>= noteS @'MLSMissingGroupInfo getGroupInfoFromRemoteConv :: - ( Member (Error FederationError) r, + ( Member (Error ConversationSubsystemError) r, + Member (Error FederationError) r, Member (E.FederationAPIAccess FederatorClient) r ) => - (Members MLSGroupInfoStaticErrors r) => Local UserId -> Remote ConvOrSubConvId -> Sem r GroupInfoData @@ -96,7 +99,10 @@ getGroupInfoFromRemoteConv lusr rcnv = do } response <- E.runFederated rcnv (fedClient @'Galley @"query-group-info" getRequest) case response of - GetGroupInfoResponseError e -> rethrowErrors @MLSGroupInfoStaticErrors e + GetGroupInfoResponseError e -> + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (T.pack . show $ e)) GetGroupInfoResponseState s -> pure . GroupInfoData diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfoCheck.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfoCheck.hs index d3bdabf3e3b..da8d051ce30 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfoCheck.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/GroupInfoCheck.hs @@ -30,7 +30,6 @@ import Polysemy.Error import Polysemy.Input import Polysemy.NonDet import Wire.API.Conversation hiding (Member) -import Wire.API.Error import Wire.API.Error.Galley import Wire.API.MLS.Credential import Wire.API.MLS.Extension @@ -42,6 +41,7 @@ import Wire.API.MLS.Serialisation import Wire.API.Team.Feature import Wire.ConversationStore import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.FeaturesConfigSubsystem (FeaturesConfigSubsystem, getFeatureForTeam) data GroupInfoMismatch = GroupInfoMismatch @@ -92,16 +92,16 @@ existingGroupStateMismatch :: (Member ConversationStore r) => ConvOrSubConv -> Sem r (Maybe GroupInfoMismatch) -existingGroupStateMismatch convOrSub = - fmap join . runErrorS @MLSMissingGroupInfo $ - do - groupInfoData <- getConvOrSubGroupInfo convOrSub.id >>= noteS @MLSMissingGroupInfo - groupInfo <- - either (\_ -> throwS @MLSMissingGroupInfo) pure $ - decodeMLS' (unGroupInfoData groupInfoData) - case groupStateMismatch convOrSub.indexMap groupInfo of - Left _ -> throwS @MLSMissingGroupInfo - Right m -> pure m +existingGroupStateMismatch convOrSub = do + result <- runError @ConversationSubsystemError $ do + groupInfoData <- getConvOrSubGroupInfo convOrSub.id >>= note ConversationSubsystemErrorMLSMissingGroupInfo + groupInfo <- + either (\_ -> throw ConversationSubsystemErrorMLSMissingGroupInfo) pure $ + decodeMLS' (unGroupInfoData groupInfoData) + case groupStateMismatch convOrSub.indexMap groupInfo of + Left _ -> throw ConversationSubsystemErrorMLSMissingGroupInfo + Right m -> pure m + pure $ fromRight Nothing result isGroupInfoCheckEnabled :: ( Member FeaturesConfigSubsystem r, diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Message.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Message.hs index 1059711effd..dbdd670778d 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Message.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Message.hs @@ -33,6 +33,7 @@ import Data.Map qualified as Map import Data.Qualified import Data.Set qualified as Set import Data.Tagged +import Data.Text qualified as Text import Data.Text.Lazy qualified as LT import Data.Tuple.Extra import Galley.Types.Error @@ -68,6 +69,7 @@ import Wire.BrigAPIAccess (BrigAPIAccess) import Wire.ConversationStore import Wire.ConversationStore.MLS.Types import Wire.ConversationSubsystem.Action +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Commit.Core (getCommitData) import Wire.ConversationSubsystem.MLS.Commit.ExternalCommit import Wire.ConversationSubsystem.MLS.Commit.InternalCommit @@ -105,13 +107,7 @@ type MLSMessageStaticErrors = ErrorS 'ConvNotFound, ErrorS 'MLSNotEnabled, ErrorS 'MLSUnsupportedMessage, - ErrorS 'MLSStaleMessage, - ErrorS 'MLSProposalNotFound, - ErrorS 'MissingLegalholdConsent, - ErrorS 'MLSCommitMissingReferences, - ErrorS 'MLSSelfRemovalNotAllowed, - ErrorS 'MLSClientSenderUserMismatch, - ErrorS 'MLSSubConvClientNotInParent + ErrorS 'MissingLegalholdConsent ] enableOutOfSyncCheckFromVersion :: Version -> EnableOutOfSyncCheck @@ -126,15 +122,8 @@ postMLSMessageFromLocalUser :: Member (ErrorS 'ConvMemberNotFound) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'MLSClientSenderUserMismatch) r, - Member (ErrorS 'MLSCommitMissingReferences) r, Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSProposalNotFound) r, - Member (ErrorS 'MLSSelfRemovalNotAllowed) r, - Member (ErrorS 'MLSStaleMessage) r, Member (ErrorS 'MLSUnsupportedMessage) r, - Member (ErrorS 'MLSSubConvClientNotInParent) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, Member (Error MLSOutOfSyncError) r, Member (Error GroupInfoDiagnostics) r ) => @@ -146,7 +135,7 @@ postMLSMessageFromLocalUser :: Sem r MLSMessageSendingStatus postMLSMessageFromLocalUser v lusr c conn smsg = do assertMLSEnabled - imsg <- noteS @'MLSUnsupportedMessage $ mkIncomingMessage smsg + imsg <- note ConversationSubsystemErrorMLSUnsupportedMessage $ mkIncomingMessage smsg (ctype, cnvOrSub) <- getConvFromGroupId imsg.groupId events <- map lcuEvent @@ -155,16 +144,13 @@ postMLSMessageFromLocalUser v lusr c conn smsg = do pure $ MLSMessageSendingStatus events t postMLSCommitBundle :: - ( Member (ErrorS MLSLegalholdIncompatible) r, - Member (ErrorS MLSIdentityMismatch) r, - Member (Error GroupInfoDiagnostics) r, + ( Member (Error GroupInfoDiagnostics) r, Member (Error MLSOutOfSyncError) r, Member (ErrorS GroupIdVersionNotSupported) r, Member (Input (Maybe GroupInfoCheckEnabled)) r, Member Random r, Member Resource r, Members MLSMessageStaticErrors r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r, HasProposalEffects r, Member MLSCommitLockStore r, Member FederationSubsystem r, @@ -190,9 +176,7 @@ postMLSCommitBundle loc qusr c ctype qConvOrSub conn oosCheck bundle = qConvOrSub postMLSCommitBundleFromLocalUser :: - ( Member (ErrorS MLSLegalholdIncompatible) r, - Member (ErrorS MLSIdentityMismatch) r, - Member (Error GroupInfoDiagnostics) r, + ( Member (Error GroupInfoDiagnostics) r, Member (Error MLSOutOfSyncError) r, Member (ErrorS GroupIdVersionNotSupported) r, Member (Input (Maybe GroupInfoCheckEnabled)) r, @@ -200,7 +184,6 @@ postMLSCommitBundleFromLocalUser :: Member Random r, Member Resource r, Members MLSMessageStaticErrors r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r, HasProposalEffects r, Member MLSCommitLockStore r, Member FederationSubsystem r, @@ -216,7 +199,7 @@ postMLSCommitBundleFromLocalUser :: Sem r MLSMessageSendingStatus postMLSCommitBundleFromLocalUser v lusr c conn bundle = do assertMLSEnabled - ibundle <- noteS @'MLSUnsupportedMessage $ mkIncomingBundle bundle + ibundle <- note ConversationSubsystemErrorMLSUnsupportedMessage $ mkIncomingBundle bundle (ctype, qConvOrSub) <- getConvFromGroupId ibundle.groupId events <- @@ -226,9 +209,7 @@ postMLSCommitBundleFromLocalUser v lusr c conn bundle = do pure $ MLSMessageSendingStatus events t postMLSCommitBundleToLocalConv :: - ( Member (ErrorS MLSLegalholdIncompatible) r, - Member (ErrorS MLSIdentityMismatch) r, - Member (Error GroupInfoDiagnostics) r, + ( Member (Error GroupInfoDiagnostics) r, Member (Error MLSOutOfSyncError) r, Member (ErrorS GroupIdVersionNotSupported) r, Member (Input EnableOutOfSyncCheck) r, @@ -236,7 +217,6 @@ postMLSCommitBundleToLocalConv :: Member Random r, Member Resource r, Members MLSMessageStaticErrors r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r, HasProposalEffects r, Member MLSCommitLockStore r, Member FederationSubsystem r, @@ -276,8 +256,8 @@ postMLSCommitBundleToLocalConv qusr c conn bundle ctype lConvOrSubId = do case resp of Left _ -> throw $ InternalErrorWithDescription "Server error. Team member must have vanished with the legal hold check" Right r -> case r.ulhsrStatus of - UserLegalHoldPending -> throwS @MLSLegalholdIncompatible - UserLegalHoldEnabled -> throwS @MLSLegalholdIncompatible + UserLegalHoldPending -> throw ConversationSubsystemErrorMLSLegalholdIncompatible + UserLegalHoldEnabled -> throw ConversationSubsystemErrorMLSLegalholdIncompatible UserLegalHoldDisabled -> pure () UserLegalHoldNoConsent -> pure () @@ -292,7 +272,7 @@ postMLSCommitBundleToLocalConv qusr c conn bundle ctype lConvOrSubId = do unless (ciphersuite == activeData.ciphersuite) $ throw $ mlsProtocolError "GroupInfo ciphersuite does not match conversation" - unless (bundle.epoch == activeData.epoch) $ throwS @'MLSStaleMessage + unless (bundle.epoch == activeData.epoch) $ throw ConversationSubsystemErrorMLSStaleMessage pure False senderIdentity <- getSenderIdentity qusr c bundle.sender lConvOrSub @@ -382,6 +362,7 @@ handleGroupInfoMismatch lConvId bundle m = postMLSCommitBundleToRemoteConv :: ( Member BrigAPIAccess r, + Member (Error ConversationSubsystemError) r, Members MLSMessageStaticErrors r, Member (Error FederationError) r, Member (Error MLSProtocolError) r, @@ -411,7 +392,7 @@ postMLSCommitBundleToRemoteConv loc qusr c con bundle ctype rConvOrSubId = do unless (bundle.epoch == Epoch 0 && ctype == One2OneConv) $ -- only members may send commit bundles to a remote conversation - flip unless (throwS @'ConvMemberNotFound) =<< checkLocalMemberRemoteConv (tUnqualified lusr) ((.conv) <$> rConvOrSubId) + flip unless (throw ConversationSubsystemErrorConvMemberNotFound) =<< checkLocalMemberRemoteConv (tUnqualified lusr) ((.conv) <$> rConvOrSubId) enableOutOfSyncCheck <- Just <$> input resp <- @@ -425,7 +406,10 @@ postMLSCommitBundleToRemoteConv loc qusr c con bundle ctype rConvOrSubId = do enableOutOfSyncCheck } case resp of - MLSMessageResponseError e -> rethrowErrors @MLSMessageStaticErrors e + MLSMessageResponseError e -> + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (Text.pack . show $ e)) MLSMessageResponseProtocolError e -> throw (mlsProtocolError e) MLSMessageResponseProposalFailure e -> throw (MLSProposalFailure e) MLSMessageResponseUnreachableBackends ds -> throw (UnreachableBackends (toList ds)) @@ -440,19 +424,7 @@ postMLSCommitBundleToRemoteConv loc qusr c con bundle ctype rConvOrSubId = do postMLSMessage :: ( HasProposalEffects r, - Member (ErrorS 'ConvAccessDenied) r, - Member (ErrorS 'ConvMemberNotFound) r, - Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MissingLegalholdConsent) r, - Member (ErrorS 'MLSClientSenderUserMismatch) r, - Member (ErrorS 'MLSCommitMissingReferences) r, - Member (ErrorS 'MLSProposalNotFound) r, - Member (ErrorS 'MLSSelfRemovalNotAllowed) r, - Member (ErrorS 'MLSStaleMessage) r, - Member (ErrorS 'MLSUnsupportedMessage) r, - Member (ErrorS 'MLSSubConvClientNotInParent) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, + Members MLSMessageStaticErrors r, Member (Error MLSOutOfSyncError) r, Member (Error GroupInfoDiagnostics) r ) => @@ -474,7 +446,7 @@ postMLSMessage loc qusr c ctype qconvOrSub con oosCheck msg = do qconvOrSub getSenderIdentity :: - ( Member (ErrorS 'MLSClientSenderUserMismatch) r, + ( Member (Error ConversationSubsystemError) r, Member (Error MLSProtocolError) r ) => Qualified UserId -> @@ -489,7 +461,7 @@ getSenderIdentity qusr c mSender lConvOrSubConv = do SenderMember idx -> do when (epoch > 0) $ do cid' <- note (mlsProtocolError "unknown sender leaf index") $ imLookup (tUnqualified lConvOrSubConv).indexMap idx - unless (cid' == cid) $ throwS @'MLSClientSenderUserMismatch + unless (cid' == cid) $ throw ConversationSubsystemErrorMLSClientSenderUserMismatch pure (Just idx) _ -> pure Nothing pure SenderIdentity {client = cid, index} @@ -497,11 +469,7 @@ getSenderIdentity qusr c mSender lConvOrSubConv = do postMLSMessageToLocalConv :: ( HasProposalEffects r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSClientSenderUserMismatch) r, - Member (ErrorS 'MLSStaleMessage) r, - Member (ErrorS 'MLSUnsupportedMessage) r, Member (Error MLSOutOfSyncError) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, Member (Input EnableOutOfSyncCheck) r ) => Qualified UserId -> @@ -521,11 +489,7 @@ postMLSMessageToLocalConv qusr c con msg ctype convOrSubId = do validateMessage :: ( HasProposalEffects r, Member (ErrorS ConvNotFound) r, - Member (ErrorS MLSClientSenderUserMismatch) r, - Member (ErrorS MLSStaleMessage) r, - Member (ErrorS MLSUnsupportedMessage) r, Member (Error MLSOutOfSyncError) r, - Member (ErrorS MLSInvalidLeafNodeSignature) r, Member (Input EnableOutOfSyncCheck) r ) => Qualified UserId -> @@ -542,8 +506,8 @@ validateMessage qusr c lConvOrSub mEpoch msg = do -- validate message case msg.content of IncomingMessageContentPublic pub -> case pub.content of - FramedContentCommit _commit -> throwS @'MLSUnsupportedMessage - FramedContentApplicationData _ -> throwS @'MLSUnsupportedMessage + FramedContentCommit _commit -> throw ConversationSubsystemErrorMLSUnsupportedMessage + FramedContentApplicationData _ -> throw ConversationSubsystemErrorMLSUnsupportedMessage -- proposal message FramedContentProposal prop -> processProposal qusr lConvOrSub msg.groupId msg.epoch pub prop @@ -552,7 +516,7 @@ validateMessage qusr c lConvOrSub mEpoch msg = do -- reject all application messages if the conv is in mixed state when (convOrSub.migrationState == MLSMigrationMixed) $ - throwS @'MLSUnsupportedMessage + throw ConversationSubsystemErrorMLSUnsupportedMessage -- reject message if the conversation is out of sync for_ convOrSub.ciphersuite $ \ciphersuite -> do @@ -574,7 +538,7 @@ validateMessage qusr c lConvOrSub mEpoch msg = do ( epochInt msg.epoch < epochInt epoch - 2 || epochInt msg.epoch > epochInt epoch ) - $ throwS @'MLSStaleMessage + $ throw ConversationSubsystemErrorMLSStaleMessage postMLSMessageToRemoteConv :: ( Members MLSMessageStaticErrors r, @@ -594,7 +558,7 @@ postMLSMessageToRemoteConv loc qusr senderClient con msg rConvOrSubId = do -- only local users can send messages to remote conversations lusr <- foldQualified loc pure (\_ -> throwS @'ConvAccessDenied) qusr -- only members may send messages to the remote conversation - flip unless (throwS @'ConvMemberNotFound) =<< checkLocalMemberRemoteConv (tUnqualified lusr) ((.conv) <$> rConvOrSubId) + flip unless (throw ConversationSubsystemErrorConvMemberNotFound) =<< checkLocalMemberRemoteConv (tUnqualified lusr) ((.conv) <$> rConvOrSubId) enableOutOfSyncCheck <- Just <$> input resp <- @@ -608,7 +572,10 @@ postMLSMessageToRemoteConv loc qusr senderClient con msg rConvOrSubId = do enableOutOfSyncCheck } case resp of - MLSMessageResponseError e -> rethrowErrors @MLSMessageStaticErrors e + MLSMessageResponseError e -> + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (Text.pack . show $ e)) MLSMessageResponseProtocolError e -> throw (mlsProtocolError e) MLSMessageResponseProposalFailure e -> throw (MLSProposalFailure e) diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Proposal.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Proposal.hs index 712fd32aee4..ce64da81255 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Proposal.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Proposal.hs @@ -66,6 +66,7 @@ import Wire.BackendNotificationQueueAccess import Wire.BrigAPIAccess import Wire.ConversationStore (ConversationStore) import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.IncomingMessage import Wire.ExternalAccess import Wire.FederationAPIAccess (FederationAPIAccess) @@ -125,9 +126,7 @@ type HasProposalEffects r = Member (Error FederationError) r, Member (Error MLSProposalFailure) r, Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSClientMismatch) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSUnsupportedProposal) r, + Member (Error ConversationSubsystemError) r, Member (Error NonFederatingBackends) r, Member (Error UnreachableBackends) r, Member ExternalAccess r, @@ -143,12 +142,9 @@ type HasProposalEffects r = derefOrCheckProposal :: ( Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSUnsupportedProposal) r, + Member (Error ConversationSubsystemError) r, Member ProposalStore r, - Member (State IndexMap) r, - Member (ErrorS 'MLSProposalNotFound) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r + Member (State IndexMap) r ) => Epoch -> CipherSuiteTag -> @@ -156,7 +152,7 @@ derefOrCheckProposal :: ProposalOrRef -> Sem r Proposal derefOrCheckProposal epoch _ciphersuite groupId (Ref ref) = do - p <- getProposal groupId epoch ref >>= noteS @'MLSProposalNotFound + p <- getProposal groupId epoch ref >>= note ConversationSubsystemErrorMLSProposalNotFound pure p.value derefOrCheckProposal _epoch ciphersuite _ (Inline p) = do im <- get @@ -165,9 +161,7 @@ derefOrCheckProposal _epoch ciphersuite _ (Inline p) = do checkProposal :: ( Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSUnsupportedProposal) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r + Member (Error ConversationSubsystemError) r ) => CipherSuiteTag -> IndexMap -> @@ -176,8 +170,8 @@ checkProposal :: checkProposal ciphersuite im p = void $ evalState im $ applyProposal ciphersuite p addProposedClient :: - ( Member (State IndexMap) r, - Member (ErrorS MLSUnsupportedProposal) r + ( Member (Error ConversationSubsystemError) r, + Member (State IndexMap) r ) => Either ClientIdentity KeyPackage -> Sem r ProposalAction @@ -193,11 +187,9 @@ addProposedClient cidOrKp = do pure (paAddClient cid idx mKp) applyProposals :: - ( Member (State IndexMap) r, - Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSUnsupportedProposal) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r + ( Member (Error MLSProtocolError) r, + Member (Error ConversationSubsystemError) r, + Member (State IndexMap) r ) => CipherSuiteTag -> [Proposal] -> @@ -208,11 +200,9 @@ applyProposals ciphersuite = . sortOn proposalProcessingStage applyProposal :: - ( Member (State IndexMap) r, - Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSUnsupportedProposal) r, - Member (ErrorS 'MLSInvalidLeafNodeIndex) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r + ( Member (Error MLSProtocolError) r, + Member (Error ConversationSubsystemError) r, + Member (State IndexMap) r ) => CipherSuiteTag -> Proposal -> @@ -221,7 +211,7 @@ applyProposal ciphersuite (AddProposal kp) = do (cs, _lifetime) <- either ( \case - InvalidLeafNodeSignature -> throwS @'MLSInvalidLeafNodeSignature + InvalidLeafNodeSignature -> throw ConversationSubsystemErrorMLSInvalidLeafNodeSignature validationError -> throw (mlsProtocolError ("Invalid key package in Add proposal: " <> (toText validationError))) ) pure @@ -232,17 +222,14 @@ applyProposal ciphersuite (AddProposal kp) = do addProposedClient (Right kp.value) applyProposal _ciphersuite (RemoveProposal idx) = do im <- get - (cid, im') <- noteS @'MLSInvalidLeafNodeIndex $ imRemoveClient im idx + (cid, im') <- note ConversationSubsystemErrorMLSInvalidLeafNodeIndex $ imRemoveClient im idx put im' pure (paRemoveClient cid idx) applyProposal _activeData _ = pure mempty processProposal :: (HasProposalEffects r) => - ( Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSStaleMessage) r, - Member (ErrorS 'MLSInvalidLeafNodeSignature) r - ) => + (Member (ErrorS 'ConvNotFound) r) => Qualified UserId -> Local ConvOrSubConv -> GroupId -> @@ -259,7 +246,7 @@ processProposal qusr lConvOrSub groupId epoch pub prop = do Nothing -> throw $ mlsProtocolError "Bare proposals at epoch 0 are not supported" Just activeData -> do -- Check if the epoch number matches that of a conversation - unless (epoch == activeData.epoch) $ throwS @'MLSStaleMessage + unless (epoch == activeData.epoch) $ throw ConversationSubsystemErrorMLSStaleMessage -- FUTUREWORK: validate the member's conversation role checkProposal activeData.ciphersuite (tUnqualified lConvOrSub).indexMap prop.value @@ -278,21 +265,21 @@ processProposal qusr lConvOrSub groupId epoch pub prop = do } getKeyPackageIdentity :: - (Member (ErrorS 'MLSUnsupportedProposal) r) => + (Member (Error ConversationSubsystemError) r) => KeyPackage -> Sem r ClientIdentity getKeyPackageIdentity = - either (\_ -> throwS @'MLSUnsupportedProposal) pure + either (\_ -> throw ConversationSubsystemErrorMLSUnsupportedProposal) pure . keyPackageIdentity isExternal :: Sender -> Bool isExternal (SenderMember _) = False isExternal _ = True --- check owner/subject of the key package exists and belongs to the user +-- check owner/subject of key package exists and belongs to the user checkExternalProposalUser :: ( Member BrigAPIAccess r, - Member (ErrorS 'MLSUnsupportedProposal) r, + Member (Error ConversationSubsystemError) r, Member (Input (Local ())) r ) => Qualified UserId -> @@ -306,14 +293,14 @@ checkExternalProposalUser qusr prop = do AddProposal kp -> do ClientIdentity {ciUser, ciClient} <- getKeyPackageIdentity kp.value -- requesting user must match key package owner - when (tUnqualified lusr /= ciUser) $ throwS @'MLSUnsupportedProposal + when (tUnqualified lusr /= ciUser) $ throw ConversationSubsystemErrorMLSUnsupportedProposal -- client referenced in key package must be one of the user's clients UserClients {userClients} <- lookupClients [ciUser] maybe - (throwS @'MLSUnsupportedProposal) - (flip when (throwS @'MLSUnsupportedProposal) . Set.null . Set.filter (== ciClient)) + (throw ConversationSubsystemErrorMLSUnsupportedProposal) + (flip when (throw ConversationSubsystemErrorMLSUnsupportedProposal) . Set.null . Set.filter (== ciClient)) $ userClients Map.!? ciUser - _ -> throwS @'MLSUnsupportedProposal + _ -> throw ConversationSubsystemErrorMLSUnsupportedProposal ) (const $ pure ()) -- FUTUREWORK: check external proposals from remote backends qusr diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Reset.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Reset.hs index 0f88ab79699..eea79ca7856 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Reset.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Reset.hs @@ -39,6 +39,7 @@ import Wire.BackendNotificationQueueAccess import Wire.BrigAPIAccess (BrigAPIAccess) import Wire.ConversationStore import Wire.ConversationSubsystem.Action +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Enabled (assertMLSEnabled) import Wire.ConversationSubsystem.MLS.Util import Wire.ConversationSubsystem.Update @@ -53,15 +54,14 @@ import Wire.TeamSubsystem (TeamSubsystem) resetMLSConversation :: ( Member Now r, Member (Input (Local ())) r, - Member (ErrorS MLSStaleMessage) r, - Member (ErrorS (ActionDenied LeaveConversation)) r, - Member (ErrorS ConvNotFound) r, Member (Error MLSProtocolError) r, Member (Error InternalError) r, + Member (Error ConversationSubsystemError) r, + Member (ErrorS MLSNotEnabled) r, + Member (ErrorS (ActionDenied LeaveConversation)) r, + Member (ErrorS ConvNotFound) r, Member (ErrorS InvalidOperation) r, - Member (ErrorS MLSFederatedResetNotSupported) r, Member (Input (Maybe (MLSKeysByPurpose MLSPrivateKeys))) r, - Member (ErrorS MLSNotEnabled) r, Member BackendNotificationQueueAccess r, Member ConversationStore r, Member (FederationAPIAccess FederatorClient) r, @@ -86,7 +86,7 @@ resetMLSConversation lusr reset = do cnv <- case qUnqualified qcnvOrSub of Conv c -> pure c - SubConv _ _ -> throwS @InvalidOperation + SubConv _ _ -> throw ConversationSubsystemErrorInvalidOperation let qcnv = qcnvOrSub $> cnv foldQualified @@ -105,11 +105,7 @@ resetMLSConversation lusr reset = do resetRemoteMLSConversation :: ( Member (Input (Local ())) r, Member P.TinyLog r, - Member (ErrorS (ActionDenied LeaveConversation)) r, - Member (ErrorS InvalidOperation) r, - Member (ErrorS ConvNotFound) r, - Member (ErrorS MLSFederatedResetNotSupported) r, - Member (ErrorS MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member (Error FederationError) r, Member (Error InternalError) r, Member BrigAPIAccess r, @@ -133,12 +129,12 @@ resetRemoteMLSConversation rcnv lusr reset = reset where handleFedError :: - ( Member (ErrorS MLSFederatedResetNotSupported) r, + ( Member (Error ConversationSubsystemError) r, Member (Error FederationError) r ) => Either FederationError x -> Sem r () handleFedError (Left (FederationCallFailure FederatorClientVersionMismatch)) = - throwS @MLSFederatedResetNotSupported + throw ConversationSubsystemErrorMLSFederatedResetNotSupported handleFedError (Left e) = throw e handleFedError (Right _) = pure () diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/SubConversation.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/SubConversation.hs index 14574b31c26..2fd8a774769 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/SubConversation.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/SubConversation.hs @@ -35,6 +35,7 @@ import Control.Arrow import Control.Monad.Codensity hiding (reset) import Data.Id import Data.Qualified +import Data.Text qualified as T import Imports import Polysemy import Polysemy.Error @@ -60,6 +61,7 @@ import Wire.API.Routes.Public.Galley.MLS import Wire.BackendNotificationQueueAccess import Wire.ConversationStore qualified as Conversation import Wire.ConversationStore.MLS.Types as Conversation +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.MLS.Conversation (mkMLSConversation) import Wire.ConversationSubsystem.MLS.Enabled (assertMLSEnabled) import Wire.ConversationSubsystem.MLS.GroupInfo (getGroupInfoFromRemoteConv) @@ -88,6 +90,7 @@ getSubConversation :: Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'MLSSubConvUnsupportedConvType) r, Member (Error FederationError) r, + Member (Error ConversationSubsystemError) r, Member (FederationAPIAccess FederatorClient) r, Member TeamSubsystem r ) => @@ -106,7 +109,7 @@ getLocalSubConversation :: ( Member Conversation.ConversationStore r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'ConvAccessDenied) r, - Member (ErrorS 'MLSSubConvUnsupportedConvType) r, + Member (Error ConversationSubsystemError) r, Member TeamSubsystem r ) => Qualified UserId -> @@ -117,7 +120,7 @@ getLocalSubConversation qusr lconv sconv = do c <- getConversationAsMember qusr lconv unless (Data.convType c == RegularConv || Data.convType c == One2OneConv) $ - throwS @'MLSSubConvUnsupportedConvType + throw ConversationSubsystemErrorMLSSubConvUnsupportedConvType msub <- Conversation.getSubConversation (tUnqualified lconv) sconv sub <- case msub of @@ -125,7 +128,7 @@ getLocalSubConversation qusr lconv sconv = do (_mlsMeta, mlsProtocol) <- noteS @'ConvNotFound (mlsMetadata c) case mlsProtocol of - MLSMigrationMixed -> throwS @'MLSSubConvUnsupportedConvType + MLSMigrationMixed -> throw ConversationSubsystemErrorMLSSubConvUnsupportedConvType MLSMigrationMLS -> pure () -- deriving this deterministically to prevent race conditions with @@ -144,7 +147,7 @@ getRemoteSubConversation :: FederationAPIAccess FederatorClient ] r, - RethrowErrors MLSGetSubConvStaticErrors r + Member (Error ConversationSubsystemError) r ) => Local UserId -> Remote ConvId -> @@ -160,7 +163,9 @@ getRemoteSubConversation lusr rcnv sconv = do } case res of GetSubConversationsResponseError e -> - rethrowErrors @MLSGetSubConvStaticErrors @r e + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (T.pack . show $ e)) GetSubConversationsResponseSuccess subconv -> pure subconv @@ -172,7 +177,8 @@ getSubConversationGroupInfo :: Input (Maybe (MLSKeysByPurpose MLSPrivateKeys)), ErrorS 'MLSNotEnabled, ErrorS 'ConvNotFound, - ErrorS 'MLSMissingGroupInfo + ErrorS 'MLSMissingGroupInfo, + Error ConversationSubsystemError ] r ) => @@ -191,7 +197,7 @@ getSubConversationGroupInfo lusr qcnvId subconv = do getSubConversationGroupInfoFromLocalConv :: ( Member Conversation.ConversationStore r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSMissingGroupInfo) r + Member (Error ConversationSubsystemError) r ) => Qualified UserId -> SubConvId -> @@ -200,7 +206,7 @@ getSubConversationGroupInfoFromLocalConv :: getSubConversationGroupInfoFromLocalConv qusr subConvId lcnvId = do void $ getLocalConvForUser qusr lcnvId Conversation.getSubConversationGroupInfo (tUnqualified lcnvId) subConvId - >>= noteS @'MLSMissingGroupInfo + >>= note ConversationSubsystemErrorMLSMissingGroupInfo type MLSDeleteSubConvStaticErrors = '[ ErrorS 'ConvAccessDenied, @@ -214,7 +220,7 @@ deleteSubConversation :: Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member (Error FederationError) r, Member (FederationAPIAccess FederatorClient) r, Member (Input (Maybe (MLSKeysByPurpose MLSPrivateKeys))) r, @@ -236,10 +242,7 @@ deleteSubConversation lusr qconv sconv reset = do qconv resetRemoteSubConversation :: - ( Member (ErrorS 'ConvAccessDenied) r, - Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSNotEnabled) r, - Member (ErrorS 'MLSStaleMessage) r, + ( Member (Error ConversationSubsystemError) r, Member (Error FederationError) r, Member (FederationAPIAccess FederatorClient) r ) => @@ -262,7 +265,10 @@ resetRemoteSubConversation lusr rcnvId scnvId reset = do rcnvId (fedClient @'Galley @"delete-sub-conversation" deleteRequest) case response of - DeleteSubConversationResponseError e -> rethrowErrors @MLSDeleteSubConvStaticErrors e + DeleteSubConversationResponseError e -> + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (T.pack . show $ e)) DeleteSubConversationResponseSuccess -> pure () type HasLeaveSubConversationEffects r = @@ -289,7 +295,7 @@ leaveSubConversation :: ( HasLeaveSubConversationEffects r, Member (Error MLSProtocolError) r, Member (Error FederationError) r, - Member (ErrorS 'MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'MLSNotEnabled) r, Member Resource r, Members LeaveSubConversationStaticErrors r, @@ -315,7 +321,7 @@ leaveSubConversation lusr cli qcnv sub = leaveLocalSubConversation :: ( HasLeaveSubConversationEffects r, Member (Error MLSProtocolError) r, - Member (ErrorS 'MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'MLSNotEnabled) r, Member (Error FederationError) r, Member Resource r, @@ -365,7 +371,8 @@ leaveRemoteSubConversation :: Error MLSProtocolError, FederationAPIAccess FederatorClient ] - r + r, + Member (Error ConversationSubsystemError) r ) => ClientIdentity -> Remote ConvId -> @@ -383,7 +390,9 @@ leaveRemoteSubConversation cid rcnv sub = do } case res of LeaveSubConversationResponseError e -> - rethrowErrors @'[ErrorS 'ConvNotFound, ErrorS 'ConvAccessDenied] e + case galleyErrorToConversationSubsystemError e of + Just cse -> throw cse + Nothing -> throw (FederationUnexpectedError (T.pack . show $ e)) LeaveSubConversationResponseProtocolError e -> throw (mlsProtocolError e) LeaveSubConversationResponseOk -> pure () @@ -392,7 +401,7 @@ resetLocalSubConversation :: ( Member Conversation.ConversationStore r, Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member Resource r, Member Conversation.MLSCommitLockStore r, Member TeamSubsystem r @@ -413,7 +422,7 @@ resetLocalSubConversation qusr lcnvId scnvId reset = do sconv <- Conversation.getSubConversation cnvId scnvId >>= noteS @'ConvNotFound let (gid, epoch) = (cnvmlsGroupId &&& cnvmlsEpoch) (scMLSData sconv) unless (reset.groupId == gid) $ throwS @'ConvNotFound - unless (reset.epoch == epoch) $ throwS @'MLSStaleMessage + unless (reset.epoch == epoch) $ throw ConversationSubsystemErrorMLSStaleMessage Conversation.removeAllMLSClients gid -- swallowing the error and starting with GroupIdGen 0 if nextGenGroupId fails diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Util.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Util.hs index 814603f64ff..ece4d194b29 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Util.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/MLS/Util.hs @@ -41,6 +41,7 @@ import Wire.API.MLS.Proposal import Wire.API.MLS.Serialisation import Wire.API.MLS.SubConversation import Wire.ConversationStore +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ProposalStore import Wire.StoredConversation @@ -101,7 +102,7 @@ withCommitLock :: forall r. ( Member Resource r, Member ConversationStore r, - Member (ErrorS 'MLSStaleMessage) r, + Member (Error ConversationSubsystemError) r, Member MLSCommitLockStore r ) => Local ConvOrSubConvId -> @@ -113,7 +114,7 @@ withCommitLock lConvOrSubId gid epoch = bracket ( acquireCommitLock gid epoch ttl >>= \lockAcquired -> when (lockAcquired == NotAcquired) $ - throwS @'MLSStaleMessage + throw ConversationSubsystemErrorMLSStaleMessage ) (const $ releaseCommitLock gid epoch) ( const $ do @@ -121,7 +122,7 @@ withCommitLock lConvOrSubId gid epoch = fromMaybe (Epoch 0) <$> case tUnqualified lConvOrSubId of Conv cnv -> getConversationEpoch cnv SubConv cnv sub -> getSubConversationEpoch cnv sub - unless (actualEpoch == epoch) $ throwS @'MLSStaleMessage + unless (actualEpoch == epoch) $ throw ConversationSubsystemErrorMLSStaleMessage k () ) where diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Message.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Message.hs index 214bdcf6ed8..41cfa40a246 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Message.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Message.hs @@ -77,6 +77,7 @@ import Wire.API.UserMap (UserMap (..)) import Wire.BackendNotificationQueueAccess import Wire.BrigAPIAccess import Wire.ConversationStore +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.Internal qualified as ConvSubsystem import Wire.ConversationSubsystem.LegalholdConflicts import Wire.ConversationSubsystem.Util @@ -262,7 +263,7 @@ postBroadcast :: ( Member BrigAPIAccess r, Member (ErrorS 'TeamNotFound) r, Member (ErrorS 'NonBindingTeam) r, - Member (ErrorS 'BroadcastLimitExceeded) r, + Member (Error ConversationSubsystemError) r, Member ExternalAccess r, Member (Input FeatureFlags) r, Member Now r, @@ -294,7 +295,7 @@ postBroadcast lusr con msg = runError $ do limit <- fromIntegral . fromRange <$> input @FanoutLimit -- If we are going to fan this out to more than limit, we want to fail early unless (Map.size rcps <= limit) $ - throwS @'BroadcastLimitExceeded + throw ConversationSubsystemErrorBroadcastLimitExceeded -- In large teams, we may still use the broadcast endpoint but only if `report_missing` -- is used and length `report_missing` < limit since we cannot fetch larger teams than -- that. @@ -343,7 +344,7 @@ postBroadcast lusr con msg = runError $ do pure otrResult {mssFailedToSend = failedToSend} where maybeFetchLimitedTeamMemberList :: - ( Member (ErrorS 'BroadcastLimitExceeded) r, + ( Member (Error ConversationSubsystemError) r, Member TeamSubsystem r ) => Int -> @@ -355,11 +356,11 @@ postBroadcast lusr con msg = runError $ do let localUserIdsInRcps = Map.keys rcps let localUserIdsToLookup = Set.toList $ Set.union (Set.fromList localUserIdsInFilter) (Set.fromList localUserIdsInRcps) unless (length localUserIdsToLookup <= limit) $ - throwS @'BroadcastLimitExceeded + throw ConversationSubsystemErrorBroadcastLimitExceeded TeamSubsystem.internalSelectTeamMembers tid localUserIdsToLookup maybeFetchAllMembersInTeam :: - ( Member (ErrorS 'BroadcastLimitExceeded) r, + ( Member (Error ConversationSubsystemError) r, Member TeamSubsystem r ) => TeamId -> @@ -367,7 +368,7 @@ postBroadcast lusr con msg = runError $ do maybeFetchAllMembersInTeam tid = do mems <- getTeamMembersForFanout tid when (mems ^. teamMemberListType == ListTruncated) $ - throwS @'BroadcastLimitExceeded + throw ConversationSubsystemErrorBroadcastLimitExceeded pure (mems ^. teamMembers) postQualifiedOtrMessage :: @@ -382,7 +383,8 @@ postQualifiedOtrMessage :: Member Now r, Member P.TinyLog r, Member NotificationSubsystem r, - Member TeamSubsystem r + Member TeamSubsystem r, + Member (Error ConversationSubsystemError) r ) => UserType -> Qualified UserId -> @@ -404,7 +406,7 @@ postQualifiedOtrMessage senderType sender mconn lcnv msg = conv <- getConversation (tUnqualified lcnv) >>= noteS @'ConvNotFound unless (protocolTag conv.protocol `elem` [ProtocolProteusTag, ProtocolMixedTag]) $ - throwS @'InvalidOperation + throw ConversationSubsystemErrorInvalidOperation let localMemberIds = (.id_) <$> conv.localMembers botMap :: BotMap diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Query.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Query.hs index ce8009d45bd..f6ff3db861c 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Query.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Query.hs @@ -102,6 +102,7 @@ import Wire.CodeStore.Code (Code (codeConversation)) import Wire.CodeStore.Code qualified as Data import Wire.ConversationStore qualified as ConversationStore import Wire.ConversationStore.MLS.Types +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.Fetch (getConversationIdsImpl) import Wire.ConversationSubsystem.MLS import Wire.ConversationSubsystem.MLS.Enabled (assertMLSEnabled, getMLSPrivateKeys, isMLSEnabled) @@ -639,8 +640,8 @@ getConversationByReusableCode :: ( Member BrigAPIAccess r, Member CodeStore r, Member ConversationStore.ConversationStore r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'CodeNotFound) r, - Member (ErrorS 'InvalidConversationPassword) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'GuestLinksDisabled) r, @@ -776,7 +777,7 @@ getMLSOne2OneOwnConversation :: Member (Error InternalError) r, Member (ErrorS 'MLSNotEnabled) r, Member (ErrorS 'NotConnected) r, - Member (ErrorS 'MLSFederatedOne2OneNotSupported) r, + Member (Error ConversationSubsystemError) r, Member (E.FederationAPIAccess FederatorClient) r, Member TeamStore r, Member P.TinyLog r, @@ -789,7 +790,7 @@ getMLSOne2OneOwnConversation :: getMLSOne2OneOwnConversation lself qother = do if isLocal lself qother then getMLSOne2OneConversationInternal lself qother - else throwS @MLSFederatedOne2OneNotSupported + else throw ConversationSubsystemErrorMLSFederatedOne2OneNotSupported getMLSOne2OneConversationInternal :: forall r. diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Update.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Update.hs index 96a798aeca3..e8713629d2b 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Update.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Update.hs @@ -79,6 +79,7 @@ import Data.Misc import Data.Qualified import Data.Set qualified as Set import Data.Singletons +import Data.Text qualified as T import Data.Vector qualified as V import Galley.Types.Error import Imports hiding (forkIO) @@ -122,6 +123,7 @@ import Wire.ConversationStore (ConversationStore) import Wire.ConversationStore qualified as E import Wire.ConversationSubsystem.Action import Wire.ConversationSubsystem.Action.Kick (kickMember) +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ConversationSubsystem.Message import Wire.ConversationSubsystem.Query qualified as Query import Wire.ConversationSubsystem.Util @@ -329,6 +331,7 @@ updateConversationHistory lusr con qcnv update = do updateConversationReceiptMode :: ( Member BrigAPIAccess r, Member ConversationStore r, + Member (Error ConversationSubsystemError) r, Member (Error FederationError) r, Member (Error InternalError) r, Member (ErrorS ('ActionDenied 'ModifyConversationReceiptMode)) r, @@ -374,7 +377,7 @@ updateRemoteConversation :: Member (Input (Local ())) r, Member ConversationStore r, Member TinyLog r, - RethrowErrors (HasConversationActionGalleyErrors tag) r, + Member (Error ConversationSubsystemError) r, Member (Error NonFederatingBackends) r, Member (Error UnreachableBackends) r, Member (Error FederationError) r, @@ -398,7 +401,10 @@ updateRemoteConversation rcnv lusr mconn action = getUpdateResult $ do Right x -> pure x convUpdate <- case response of ConversationUpdateResponseNoChanges -> throw NoChanges - ConversationUpdateResponseError err' -> raise $ rethrowErrors @(HasConversationActionGalleyErrors tag) err' + ConversationUpdateResponseError err' -> + case galleyErrorToConversationSubsystemError err' of + Just cse -> throw cse + Nothing -> throw $ FederationUnexpectedError (T.pack . show $ err') ConversationUpdateResponseUpdate convUpdate -> pure convUpdate ConversationUpdateResponseNonFederatingBackends e -> throw e ConversationUpdateResponseUnreachableBackends e -> throw e @@ -466,7 +472,7 @@ addCodeUnqualified :: Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'GuestLinksDisabled) r, - Member (ErrorS 'CreateConversationCodeConflict) r, + Member (Error ConversationSubsystemError) r, Member E.ExternalAccess r, Member NotificationSubsystem r, Member (Input (Local ())) r, @@ -495,7 +501,7 @@ addCode :: Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'GuestLinksDisabled) r, - Member (ErrorS 'CreateConversationCodeConflict) r, + Member (Error ConversationSubsystemError) r, Member E.ExternalAccess r, Member HashPassword r, Member NotificationSubsystem r, @@ -533,7 +539,7 @@ addCode lusr mbZHost mZcon lcnv mReq = do pure $ CodeAdded event -- In case conversation already has a code this case covers the allowed no-ops Just (code, mPw) -> do - when (isJust mPw || isJust (mReq >>= (.password))) $ throwS @'CreateConversationCodeConflict + when (isJust mPw || isJust (mReq >>= (.password))) $ throw ConversationSubsystemErrorCreateConversationCodeConflict pure $ CodeAlreadyExisted (mkConversationCodeInfo (isJust mPw) (codeKey code) (codeValue code) convUri) where ensureGuestsOrNonTeamMembersAllowed :: StoredConversation -> Sem r () @@ -621,9 +627,9 @@ checkReusableCode :: ( Member CodeStore r, Member ConversationStore r, Member FeaturesConfigSubsystem r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'CodeNotFound) r, Member (ErrorS 'ConvNotFound) r, - Member (ErrorS 'InvalidConversationPassword) r, Member HashPassword r, Member RateLimit r ) => @@ -642,6 +648,7 @@ updateConversationProtocolWithLocalUser :: Member (ErrorS 'ConvInvalidProtocolTransition) r, Member (ErrorS ('ActionDenied 'LeaveConversation)) r, Member (ErrorS 'InvalidOperation) r, + Member (Error ConversationSubsystemError) r, Member (Error FederationError) r, Member (ErrorS 'MLSMigrationCriteriaNotSatisfied) r, Member (Error InternalError) r, @@ -684,6 +691,7 @@ updateConversationProtocolWithLocalUser lusr conn qcnv (P.ProtocolUpdate newProt updateChannelAddPermission :: ( Member ConversationStore r, + Member (Error ConversationSubsystemError) r, Member (ErrorS ('ActionDenied 'ModifyAddPermission)) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'InvalidOperation) r, @@ -726,8 +734,8 @@ joinConversationByReusableCode :: ( Member BrigAPIAccess r, Member CodeStore r, Member ConversationStore r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'CodeNotFound) r, - Member (ErrorS 'InvalidConversationPassword) r, Member (ErrorS 'ConvAccessDenied) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'GuestLinksDisabled) r, @@ -1061,7 +1069,7 @@ updateOtherMemberLocalConv :: ( Member ConversationStore r, Member (Error FederationError) r, Member (ErrorS ('ActionDenied 'ModifyOtherConversationMember)) r, - Member (ErrorS 'InvalidTarget) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'InvalidOperation) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'ConvMemberNotFound) r, @@ -1079,7 +1087,7 @@ updateOtherMemberLocalConv :: Sem r () updateOtherMemberLocalConv lcnv lusr con qvictim update = void . getUpdateResult . fmap lcuEvent $ do when (tUntagged lusr == qvictim) $ - throwS @'InvalidTarget + throw ConversationSubsystemErrorInvalidTarget updateLocalConversationMemberUpdate lcnv (tUntagged lusr) (Just con) $ ConversationMemberUpdate qvictim update @@ -1087,7 +1095,7 @@ updateOtherMember :: ( Member ConversationStore r, Member (Error FederationError) r, Member (ErrorS ('ActionDenied 'ModifyOtherConversationMember)) r, - Member (ErrorS 'InvalidTarget) r, + Member (Error ConversationSubsystemError) r, Member (ErrorS 'InvalidOperation) r, Member (ErrorS 'ConvNotFound) r, Member (ErrorS 'ConvMemberNotFound) r, @@ -1276,6 +1284,7 @@ postProteusMessage :: Member ConversationStore r, Member (E.FederationAPIAccess FederatorClient) r, Member (Error FederationError) r, + Member (Error ConversationSubsystemError) r, Member BackendNotificationQueueAccess r, Member NotificationSubsystem r, Member E.ExternalAccess r, @@ -1301,7 +1310,7 @@ postProteusBroadcast :: ( Member BrigAPIAccess r, Member (ErrorS 'TeamNotFound) r, Member (ErrorS 'NonBindingTeam) r, - Member (ErrorS 'BroadcastLimitExceeded) r, + Member (Error ConversationSubsystemError) r, Member NotificationSubsystem r, Member E.ExternalAccess r, Member (Input FeatureFlags) r, @@ -1356,6 +1365,7 @@ postBotMessageUnqualified :: Member ConversationStore r, Member E.ExternalAccess r, Member (E.FederationAPIAccess FederatorClient) r, + Member (Error ConversationSubsystemError) r, Member BackendNotificationQueueAccess r, Member NotificationSubsystem r, Member (Input (Local ())) r, @@ -1385,7 +1395,7 @@ postOtrBroadcastUnqualified :: ( Member BrigAPIAccess r, Member (ErrorS 'TeamNotFound) r, Member (ErrorS 'NonBindingTeam) r, - Member (ErrorS 'BroadcastLimitExceeded) r, + Member (Error ConversationSubsystemError) r, Member NotificationSubsystem r, Member E.ExternalAccess r, Member (Input FeatureFlags) r, @@ -1413,6 +1423,7 @@ postOtrMessageUnqualified :: Member E.UserClientIndexStore r, Member ConversationStore r, Member (E.FederationAPIAccess FederatorClient) r, + Member (Error ConversationSubsystemError) r, Member BackendNotificationQueueAccess r, Member E.ExternalAccess r, Member NotificationSubsystem r, diff --git a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Util.hs b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Util.hs index 78d70193979..67d2d5cc941 100644 --- a/libs/wire-subsystems/src/Wire/ConversationSubsystem/Util.hs +++ b/libs/wire-subsystems/src/Wire/ConversationSubsystem/Util.hs @@ -49,7 +49,7 @@ import Polysemy.Input import Polysemy.TinyLog (TinyLog) import Polysemy.TinyLog qualified as P import System.Logger.Message (msg, val, (+++)) -import Wire.API.Connection +import Wire.API.Connection hiding (MissingLegalholdConsent) import Wire.API.Conversation hiding (Member, cnvAccess, cnvAccessRoles, cnvName, cnvType) import Wire.API.Conversation qualified as Public import Wire.API.Conversation.Action @@ -81,6 +81,7 @@ import Wire.BrigAPIAccess import Wire.CodeStore import Wire.CodeStore.Code as DataTypes import Wire.ConversationStore +import Wire.ConversationSubsystem.Errors (ConversationSubsystemError (..)) import Wire.ExternalAccess import Wire.FederationAPIAccess import Wire.FederationSubsystem (ensureNoUnreachableBackends) @@ -649,7 +650,7 @@ newConversationEventPush st e users = verifyReusableCode :: ( Member CodeStore r, Member (ErrorS 'CodeNotFound) r, - Member (ErrorS 'InvalidConversationPassword) r, + Member (Error ConversationSubsystemError) r, Member HashPassword r, Member RateLimit r ) => @@ -666,9 +667,9 @@ verifyReusableCode rateLimitKey checkPw mPtpw convCode = do throwS @'CodeNotFound case (checkPw, mPtpw, mPw) of (True, Just ptpw, Just pw) -> do - unlessM (HashPassword.verifyPassword rateLimitKey ptpw pw) $ throwS @'InvalidConversationPassword + unlessM (HashPassword.verifyPassword rateLimitKey ptpw pw) $ throw ConversationSubsystemErrorInvalidConversationPassword (True, Nothing, Just _) -> - throwS @'InvalidConversationPassword + throw ConversationSubsystemErrorInvalidConversationPassword (_, _, _) -> pure () pure c @@ -986,6 +987,94 @@ getUpdateResult = fmap (either (const Unchanged) Updated) . runError -------------------------------------------------------------------------------- -- Handling remote errors +-- | Convert GalleyError from federation protocol to ConversationSubsystemError +-- Returns Nothing for GalleyError values that don't have ConversationSubsystemError equivalents +galleyErrorToConversationSubsystemError :: GalleyError -> Maybe ConversationSubsystemError +galleyErrorToConversationSubsystemError = \case + ConvAccessDenied -> Just ConversationSubsystemErrorConvAccessDenied + NotATeamMember -> Just ConversationSubsystemErrorNotATeamMember + InvalidOperation -> Just ConversationSubsystemErrorInvalidOperation + NotConnected -> Just ConversationSubsystemErrorNotConnected + MLSNotEnabled -> Just ConversationSubsystemErrorMLSNotEnabled + MLSNonEmptyMemberList -> Just ConversationSubsystemErrorMLSNonEmptyMemberList + MissingLegalholdConsent -> Just ConversationSubsystemErrorMissingLegalholdConsent + NonBindingTeam -> Just ConversationSubsystemErrorNonBindingTeam + NoBindingTeamMembers -> Just ConversationSubsystemErrorNoBindingTeamMembers + TeamNotFound -> Just ConversationSubsystemErrorTeamNotFound + ConvNotFound -> Just ConversationSubsystemErrorConvNotFound + ChannelsNotEnabled -> Just ConversationSubsystemErrorChannelsNotEnabled + NotAnMlsConversation -> Just ConversationSubsystemErrorNotAnMlsConversation + MLSLegalholdIncompatible -> Just ConversationSubsystemErrorMLSLegalholdIncompatible + MLSIdentityMismatch -> Just ConversationSubsystemErrorMLSIdentityMismatch + MLSUnsupportedMessage -> Just ConversationSubsystemErrorMLSUnsupportedMessage + MLSStaleMessage -> Just ConversationSubsystemErrorMLSStaleMessage + MLSProposalNotFound -> Just ConversationSubsystemErrorMLSProposalNotFound + MLSCommitMissingReferences -> Just ConversationSubsystemErrorMLSCommitMissingReferences + MLSSelfRemovalNotAllowed -> Just ConversationSubsystemErrorMLSSelfRemovalNotAllowed + MLSClientSenderUserMismatch -> Just ConversationSubsystemErrorMLSClientSenderUserMismatch + MLSSubConvClientNotInParent -> Just ConversationSubsystemErrorMLSSubConvClientNotInParent + MLSInvalidLeafNodeSignature -> Just ConversationSubsystemErrorMLSInvalidLeafNodeSignature + MLSClientMismatch -> Just ConversationSubsystemErrorMLSClientMismatch + MLSInvalidLeafNodeIndex -> Just ConversationSubsystemErrorMLSInvalidLeafNodeIndex + MLSUnsupportedProposal -> Just ConversationSubsystemErrorMLSUnsupportedProposal + GroupIdVersionNotSupported -> Just ConversationSubsystemErrorGroupIdVersionNotSupported + ConvMemberNotFound -> Just ConversationSubsystemErrorConvMemberNotFound + HistoryNotSupported -> Just ConversationSubsystemErrorHistoryNotSupported + MLSGroupConversationMismatch -> Just ConversationSubsystemErrorLSGroupConversationMismatch + MLSFederatedResetNotSupported -> Just ConversationSubsystemErrorMLSFederatedResetNotSupported + MLSSubConvUnsupportedConvType -> Just ConversationSubsystemErrorMLSSubConvUnsupportedConvType + TeamMemberNotFound -> Just ConversationSubsystemErrorTeamMemberNotFound + AccessDenied -> Just ConversationSubsystemErrorAccessDenied + MLSMissingGroupInfo -> Just ConversationSubsystemErrorMLSMissingGroupInfo + CodeNotFound -> Just ConversationSubsystemErrorCodeNotFound + InvalidConversationPassword -> Just ConversationSubsystemErrorInvalidConversationPassword + GuestLinksDisabled -> Just ConversationSubsystemErrorGuestLinksDisabled + MLSFederatedOne2OneNotSupported -> Just ConversationSubsystemErrorMLSFederatedOne2OneNotSupported + TooManyMembers -> Just ConversationSubsystemErrorTooManyMembers + CreateConversationCodeConflict -> Just ConversationSubsystemErrorCreateConversationCodeConflict + InvalidTarget -> Just ConversationSubsystemErrorInvalidTarget + MLSReadReceiptsNotAllowed -> Just ConversationSubsystemErrorMLSReadReceiptsNotAllowed + InvalidTargetAccess -> Just ConversationSubsystemErrorInvalidTargetAccess + ConvInvalidProtocolTransition -> Just ConversationSubsystemErrorConvInvalidProtocolTransition + MLSMigrationCriteriaNotSatisfied -> Just ConversationSubsystemErrorMLSMigrationCriteriaNotSatisfied + MLSProtocolErrorTag -> Nothing + -- GalleyError values that don't have ConversationSubsystemError equivalents + InvalidAction -> Nothing + BroadcastLimitExceeded -> Nothing + UserBindingExists -> Nothing + NoAddToBinding -> Nothing + TooManyTeamMembers -> Nothing + TooManyTeamAdmins -> Nothing + MissingPermission _ -> Nothing + ActionDenied _ -> Nothing + MLSDuplicatePublicKey -> Nothing + MLSWelcomeMismatch -> Nothing + MLSUnexpectedSenderClient -> Nothing + NoBindingTeam -> Nothing + NotAOneMemberTeam -> Nothing + InvalidPermissions -> Nothing + InvalidTeamStatusUpdate -> Nothing + CustomBackendNotFound -> Nothing + DeleteQueueFull -> Nothing + TeamSearchVisibilityNotEnabled -> Nothing + CannotEnableLegalHoldServiceLargeTeam -> Nothing + MissingLegalholdConsentOldClients -> Nothing + NoUserLegalHoldConsent -> Nothing + LegalHoldNotEnabled -> Nothing + LegalHoldDisableUnimplemented -> Nothing + LegalHoldServiceInvalidKey -> Nothing + LegalHoldServiceBadResponse -> Nothing + UserLegalHoldAlreadyEnabled -> Nothing + LegalHoldServiceNotRegistered -> Nothing + LegalHoldCouldNotBlockConnections -> Nothing + UserLegalHoldIllegalOperation -> Nothing + TooManyTeamMembersOnTeamWithLegalhold -> Nothing + NoLegalHoldDeviceAllocated -> Nothing + UserLegalHoldNotPending -> Nothing + BulkGetMemberLimitExceeded -> Nothing + InvalidTeamNotificationId -> Nothing + MeetingNotFound -> Nothing + class RethrowErrors (effs :: EffectRow) r where rethrowErrors :: GalleyError -> Sem r a diff --git a/services/background-worker/src/Wire/BackgroundWorker/Jobs/Registry.hs b/services/background-worker/src/Wire/BackgroundWorker/Jobs/Registry.hs index 842dec6ec80..d539b7467a4 100644 --- a/services/background-worker/src/Wire/BackgroundWorker/Jobs/Registry.hs +++ b/services/background-worker/src/Wire/BackgroundWorker/Jobs/Registry.hs @@ -53,7 +53,7 @@ import System.Logger.Class qualified as Log import URI.ByteString (uriPath) import Wire.API.BackgroundJobs (Job (..)) import Wire.API.Conversation.Config (ConversationSubsystemConfig (..)) -import Wire.API.Error (APIError (toResponse), DynError (..)) +import Wire.API.Error (APIError (toResponse)) import Wire.API.Error.Galley import Wire.API.Federation.Error (FederationError) import Wire.API.MLS.Keys (MLSKeysByPurpose, MLSPrivateKeys) @@ -203,7 +203,6 @@ dispatchJob job = do . runDelay . resourceToIOFinal . runError - . mapError @DynError (.eMessage) . mapError @JSONResponse (T.pack . show . (.value)) . mapError @ConversationSubsystemError toResponse . mapError @ClientError (T.pack . displayException)