From e935501b756f1d0328a4ced520aa7d7408c944c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:17:00 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20asset=20restriction=20[WPB-9947]=20(#283?= =?UTF-8?q?1)=20(#2856)=20=F0=9F=8D=92=20(#2862)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Commit with unresolved merge conflicts * asset restriction * fix tests --------- Co-authored-by: Mohamad Jaara --- .../kalium/logic/feature/UserSessionScope.kt | 1 + .../asset/ScheduleNewAssetMessageUseCase.kt | 30 ++- .../logic/feature/message/MessageScope.kt | 11 +- .../ScheduleNewAssetMessageUseCaseTest.kt | 181 ++++++++++++++++++ .../managed/ConversationRepository.kt | 63 +++--- 5 files changed, 258 insertions(+), 28 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 21bdabd6477..c2dd1382ff7 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -1767,6 +1767,7 @@ class UserSessionScope internal constructor( messageMetadataRepository, staleEpochVerifier, legalHoldHandler, + observeFileSharingStatus, this, userScopedLogger, ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCase.kt index f18cde3919a..c6b2a1ff0df 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCase.kt @@ -24,6 +24,7 @@ import com.wire.kalium.cryptography.utils.AES256Key import com.wire.kalium.cryptography.utils.SHA256Key import com.wire.kalium.cryptography.utils.generateRandomAES256Key import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.configuration.FileSharingStatus import com.wire.kalium.logic.data.asset.AssetRepository import com.wire.kalium.logic.data.asset.AssetTransferStatus import com.wire.kalium.logic.data.asset.UploadedAssetId @@ -44,6 +45,7 @@ import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.message.MessageSendFailureHandler import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase +import com.wire.kalium.logic.feature.user.ObserveFileSharingStatusUseCase import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap import com.wire.kalium.logic.functional.fold @@ -108,12 +110,14 @@ internal class ScheduleNewAssetMessageUseCaseImpl( private val userPropertyRepository: UserPropertyRepository, private val selfDeleteTimer: ObserveSelfDeletionTimerSettingsForConversationUseCase, private val scope: CoroutineScope, + private val observeFileSharingStatus: ObserveFileSharingStatusUseCase, + private val validateAssetMimeTypeUseCase: ValidateAssetMimeTypeUseCase, private val dispatcher: KaliumDispatcher, ) : ScheduleNewAssetMessageUseCase { private var outGoingAssetUploadJob: Job? = null - @Suppress("LongMethod") + @Suppress("LongMethod", "ReturnCount") override suspend fun invoke( conversationId: ConversationId, assetDataPath: Path, @@ -124,6 +128,18 @@ internal class ScheduleNewAssetMessageUseCaseImpl( assetHeight: Int?, audioLengthInMs: Long ): ScheduleNewAssetMessageResult { + observeFileSharingStatus().first().also { + when (it.state) { + FileSharingStatus.Value.Disabled -> return ScheduleNewAssetMessageResult.Failure.DisabledByTeam + FileSharingStatus.Value.EnabledAll -> { /* no-op*/ } + + is FileSharingStatus.Value.EnabledSome -> if (!validateAssetMimeTypeUseCase(assetMimeType, it.state.allowedType)) { + kaliumLogger.e("The asset message trying to be processed has invalid content data") + return ScheduleNewAssetMessageResult.Failure.RestrictedFileType + } + } + } + slowSyncRepository.slowSyncStatus.first { it is SlowSyncStatus.Complete } @@ -165,7 +181,7 @@ internal class ScheduleNewAssetMessageUseCaseImpl( } } }.fold({ - ScheduleNewAssetMessageResult.Failure(it) + ScheduleNewAssetMessageResult.Failure.Generic(it) }, { (_, message) -> ScheduleNewAssetMessageResult.Success(message.id) }) @@ -345,9 +361,13 @@ internal class ScheduleNewAssetMessageUseCaseImpl( } } -sealed class ScheduleNewAssetMessageResult { - class Success(val messageId: String) : ScheduleNewAssetMessageResult() - class Failure(val coreFailure: CoreFailure) : ScheduleNewAssetMessageResult() +sealed interface ScheduleNewAssetMessageResult { + data class Success(val messageId: String) : ScheduleNewAssetMessageResult + sealed interface Failure : ScheduleNewAssetMessageResult { + data class Generic(val coreFailure: CoreFailure) : Failure + data object DisabledByTeam : Failure + data object RestrictedFileType : Failure + } } private data class AssetMessageMetadata( diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageScope.kt index 586549ef2b7..175efd55352 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageScope.kt @@ -61,6 +61,8 @@ import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageUseCaseImpl import com.wire.kalium.logic.feature.asset.UpdateAssetMessageTransferStatusUseCase import com.wire.kalium.logic.feature.asset.UpdateAssetMessageTransferStatusUseCaseImpl import com.wire.kalium.logic.feature.message.composite.SendButtonActionConfirmationMessageUseCase +import com.wire.kalium.logic.feature.asset.ValidateAssetMimeTypeUseCase +import com.wire.kalium.logic.feature.asset.ValidateAssetMimeTypeUseCaseImpl import com.wire.kalium.logic.feature.message.composite.SendButtonActionMessageUseCase import com.wire.kalium.logic.feature.message.composite.SendButtonMessageUseCase import com.wire.kalium.logic.feature.message.confirmation.ConfirmationDeliveryHandler @@ -82,6 +84,7 @@ import com.wire.kalium.logic.feature.message.ephemeral.EphemeralMessageDeletionH import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase import com.wire.kalium.logic.feature.sessionreset.ResetSessionUseCase import com.wire.kalium.logic.feature.sessionreset.ResetSessionUseCaseImpl +import com.wire.kalium.logic.feature.user.ObserveFileSharingStatusUseCase import com.wire.kalium.logic.sync.SyncManager import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler import com.wire.kalium.logic.util.MessageContentEncoder @@ -118,8 +121,9 @@ class MessageScope internal constructor( private val messageMetadataRepository: MessageMetadataRepository, private val staleEpochVerifier: StaleEpochVerifier, private val legalHoldHandler: LegalHoldHandler, + private val observeFileSharingStatusUseCase: ObserveFileSharingStatusUseCase, private val scope: CoroutineScope, - private val kaliumLogger: KaliumLogger, + kaliumLogger: KaliumLogger, internal val dispatcher: KaliumDispatcher = KaliumDispatcherImpl, private val legalHoldStatusMapper: LegalHoldStatusMapper = LegalHoldStatusMapperImpl ) { @@ -155,6 +159,9 @@ class MessageScope internal constructor( protoContentMapper = protoContentMapper ) + private val validateAssetMimeTypeUseCase: ValidateAssetMimeTypeUseCase + get() = ValidateAssetMimeTypeUseCaseImpl() + private val messageContentEncoder = MessageContentEncoder() private val messageSendingInterceptor: MessageSendingInterceptor get() = MessageSendingInterceptorImpl(messageContentEncoder, messageRepository) @@ -269,6 +276,8 @@ class MessageScope internal constructor( userPropertyRepository, observeSelfDeletingMessages, scope, + observeFileSharingStatusUseCase, + validateAssetMimeTypeUseCase, dispatcher ) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCaseTest.kt index bc7bc68dc6e..94ad32bd51c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/asset/ScheduleNewAssetMessageUseCaseTest.kt @@ -22,6 +22,7 @@ import com.wire.kalium.cryptography.utils.SHA256Key import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.FileSharingStatus import com.wire.kalium.logic.data.asset.AssetRepository import com.wire.kalium.logic.data.asset.AssetTransferStatus import com.wire.kalium.logic.data.asset.FakeKaliumFileSystem @@ -41,6 +42,7 @@ import com.wire.kalium.logic.data.sync.SlowSyncStatus import com.wire.kalium.logic.feature.message.MessageSendFailureHandler import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase +import com.wire.kalium.logic.feature.user.ObserveFileSharingStatusUseCase import com.wire.kalium.logic.framework.TestAsset.dummyUploadedAssetId import com.wire.kalium.logic.framework.TestAsset.mockedLongAssetData import com.wire.kalium.logic.functional.Either @@ -59,6 +61,7 @@ import io.mockative.matches import io.mockative.mock import io.mockative.once import io.mockative.twice +import io.mockative.verify import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -93,6 +96,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withObserveMessageVisibility() .withDeleteAssetLocally() .withSelfDeleteTimer(SelfDeletionTimer.Disabled) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -126,6 +130,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withDeleteAssetLocally() .withObserveMessageVisibility() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -159,6 +164,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -197,6 +203,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -244,6 +251,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withObserveMessageVisibility() .withDeleteAssetLocally() .withUpdateMessageAssetTransferStatus(UpdateTransferStatusResult.Success) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -275,6 +283,51 @@ class ScheduleNewAssetMessageUseCaseTest { }.wasInvoked(exactly = once) } + @Test + fun givenASuccessfulSendAssetMessageRequest_whenCheckingTheMessageRepository_thenTheAssetIsMarkedAsSavedInternally() = + runTest(testDispatcher.default) { + // Given + val assetToSend = mockedLongAssetData() + val assetName = "some-asset.txt" + val conversationId = ConversationId("some-convo-id", "some-domain-id") + val dataPath = fakeKaliumFileSystem.providePersistentAssetPath(assetName) + val expectedAssetId = dummyUploadedAssetId + val expectedAssetSha256 = SHA256Key("some-asset-sha-256".toByteArray()) + val (arrangement, sendAssetUseCase) = Arrangement(this) + .withSuccessfulResponse(expectedAssetId, expectedAssetSha256) + .withSelfDeleteTimer(SelfDeletionTimer.Disabled) + .withObserveMessageVisibility() + .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) + .arrange() + + // When + sendAssetUseCase.invoke( + conversationId = conversationId, + assetDataPath = dataPath, + assetDataSize = assetToSend.size.toLong(), + assetName = assetName, + assetMimeType = "text/plain", + assetWidth = null, + assetHeight = null, + audioLengthInMs = 0 + ) + advanceUntilIdle() + + // Then + coVerify { arrangement.assetDataSource.persistAsset(any(), any(), any(), any(), any()) } + .wasInvoked(exactly = once) + coVerify { + arrangement.persistMessage( + matches { + val content = it.content + content is MessageContent.Asset + } + ) + }.wasInvoked(exactly = twice) + + } + @Test fun givenAnErrorAtInitialAssetPersistCall_whenCheckingTheMessageRepository_thenTheAssetUploadStatusIsMarkedAsFailed() = runTest(testDispatcher.default) { @@ -288,6 +341,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -335,6 +389,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withObserveMessageVisibility() .withDeleteAssetLocally() .withUpdateMessageAssetTransferStatus(UpdateTransferStatusResult.Success) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -388,6 +443,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -439,6 +495,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Disabled) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -484,6 +541,7 @@ class ScheduleNewAssetMessageUseCaseTest { .withSelfDeleteTimer(SelfDeletionTimer.Enabled(expectedDuration)) .withObserveMessageVisibility() .withDeleteAssetLocally() + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledAll) .arrange() // When @@ -512,6 +570,109 @@ class ScheduleNewAssetMessageUseCaseTest { } } + @Test + fun givenFileSendingRestrictedByTeam_whenSending_thenReturnDisabledByTeam() = runTest { + // Given + val assetToSend = mockedLongAssetData() + val assetName = "some-asset.txt" + val inputDataPath = fakeKaliumFileSystem.providePersistentAssetPath(assetName) + val conversationId = ConversationId("some-convo-id", "some-domain-id") + val (_, sendAssetUseCase) = Arrangement(this) + .withStoredData(assetToSend, inputDataPath) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.Disabled) + .arrange() + + // When + val result = sendAssetUseCase.invoke( + conversationId = conversationId, + assetDataPath = inputDataPath, + assetDataSize = assetToSend.size.toLong(), + assetName = assetName, + assetMimeType = "text/plain", + assetWidth = null, + assetHeight = null, + audioLengthInMs = 0 + ) + advanceUntilIdle() + + // Then + assertTrue(result is ScheduleNewAssetMessageResult.Failure.DisabledByTeam) + } + + @Test + fun givenAseetMimeTypeRestricted_whenSending_thenReturnRestrictedFileType() = runTest { + // Given + val assetToSend = mockedLongAssetData() + val assetName = "some-asset.txt" + val inputDataPath = fakeKaliumFileSystem.providePersistentAssetPath(assetName) + val conversationId = ConversationId("some-convo-id", "some-domain-id") + val (arrangement, sendAssetUseCase) = Arrangement(this) + .withStoredData(assetToSend, inputDataPath) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledSome(listOf("png"))) + .withValidateAsseMimeTypeResult(false) + .arrange() + + // When + val result = sendAssetUseCase.invoke( + conversationId = conversationId, + assetDataPath = inputDataPath, + assetDataSize = assetToSend.size.toLong(), + assetName = assetName, + assetMimeType = "text/plain", + assetWidth = null, + assetHeight = null, + audioLengthInMs = 0 + ) + advanceUntilIdle() + + // Then + assertTrue(result is ScheduleNewAssetMessageResult.Failure.RestrictedFileType) + + verify { + arrangement.validateAssetMimeTypeUseCase(eq("text/plain"), eq(listOf("png"))) + }.wasInvoked(exactly = once) + } + + @Test + fun givenAssetMimeTypeRestrictedAndFileAllowed_whenSending_thenReturnSendTheFile() = runTest(testDispatcher.default) { + // Given + val assetToSend = mockedLongAssetData() + val assetName = "some-asset.txt" + val inputDataPath = fakeKaliumFileSystem.providePersistentAssetPath(assetName) + val expectedAssetId = dummyUploadedAssetId + val expectedAssetSha256 = SHA256Key("some-asset-sha-256".toByteArray()) + val conversationId = ConversationId("some-convo-id", "some-domain-id") + val (arrangement, sendAssetUseCase) = Arrangement(this) + .withStoredData(assetToSend, inputDataPath) + .withSuccessfulResponse(expectedAssetId, expectedAssetSha256) + .withObserveFileSharingStatusResult(FileSharingStatus.Value.EnabledSome(listOf("png"))) + .withValidateAsseMimeTypeResult(true) + .withSelfDeleteTimer(SelfDeletionTimer.Disabled) + .withObserveMessageVisibility() + .withDeleteAssetLocally() + .arrange() + + // When + val result = sendAssetUseCase.invoke( + conversationId = conversationId, + assetDataPath = inputDataPath, + assetDataSize = assetToSend.size.toLong(), + assetName = assetName, + assetMimeType = "image/png", + assetWidth = null, + assetHeight = null, + audioLengthInMs = 0 + ) + advanceUntilIdle() + + // Then + assertTrue(result is ScheduleNewAssetMessageResult.Success) + + verify { + arrangement.validateAssetMimeTypeUseCase(eq("image/png"), eq(listOf("png"))) + }.wasInvoked(exactly = once) + } + private class Arrangement(val coroutineScope: CoroutineScope) { @Mock @@ -544,6 +705,12 @@ class ScheduleNewAssetMessageUseCaseTest { @Mock private val messageRepository: MessageRepository = mock(MessageRepository::class) + @Mock + val validateAssetMimeTypeUseCase: ValidateAssetMimeTypeUseCase = mock(ValidateAssetMimeTypeUseCase::class) + + @Mock + val observerFileSharingStatusUseCase: ObserveFileSharingStatusUseCase = mock(ObserveFileSharingStatusUseCase::class) + val someClientId = ClientId("some-client-id") val completeStateFlow = MutableStateFlow(SlowSyncStatus.Complete).asStateFlow() @@ -554,6 +721,18 @@ class ScheduleNewAssetMessageUseCaseTest { }.returns(enabled) } + fun withValidateAsseMimeTypeResult(result: Boolean) = apply { + every { + validateAssetMimeTypeUseCase.invoke(any(), any()) + }.returns(result) + } + + fun withObserveFileSharingStatusResult(result: FileSharingStatus.Value) = apply { + every { + observerFileSharingStatusUseCase.invoke() + }.returns(flowOf(FileSharingStatus(result, false))) + } + fun withStoredData(data: ByteArray, dataPath: Path): Arrangement { fakeKaliumFileSystem.sink(dataPath).buffer().use { it.write(data) @@ -704,6 +883,8 @@ class ScheduleNewAssetMessageUseCaseTest { userPropertyRepository, observeSelfDeletionTimerSettingsForConversation, coroutineScope, + observerFileSharingStatusUseCase, + validateAssetMimeTypeUseCase, testDispatcher ).also { withToggleReadReceiptsStatus() diff --git a/testservice/src/main/kotlin/com/wire/kalium/testservice/managed/ConversationRepository.kt b/testservice/src/main/kotlin/com/wire/kalium/testservice/managed/ConversationRepository.kt index a4d5c5ff61e..3e717d1ffe5 100644 --- a/testservice/src/main/kotlin/com/wire/kalium/testservice/managed/ConversationRepository.kt +++ b/testservice/src/main/kotlin/com/wire/kalium/testservice/managed/ConversationRepository.kt @@ -482,7 +482,7 @@ sealed class ConversationRepository { throw WebApplicationException("Instance ${instance.instanceId}: Could not get receipts from message") } - @Suppress("LongParameterList", "LongMethod", "ThrowsCount") + @Suppress("LongParameterList", "LongMethod", "ThrowsCount", "ComplexMethod") suspend fun sendFile( instance: Instance, conversationId: ConversationId, @@ -537,16 +537,28 @@ sealed class ConversationRepository { } when (sendResult) { is ScheduleNewAssetMessageResult.Failure -> { - if (sendResult.coreFailure is StorageFailure.Generic) { - val rootCause = (sendResult.coreFailure as StorageFailure.Generic) - .rootCause.message - Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("Instance ${instance.instanceId}: Sending failed with $rootCause") - .build() - } else { - Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("Instance ${instance.instanceId}: Sending file $fileName failed") - .build() + // if the IDE tels you that this casting is unnecessary + // first check kotlin version + // if version < 2 then casting is necessary + // if version >= 2 then casting is unnecessary + when (val result = sendResult as ScheduleNewAssetMessageResult.Failure) { + ScheduleNewAssetMessageResult.Failure.RestrictedFileType, + ScheduleNewAssetMessageResult.Failure.DisabledByTeam -> { + throw WebApplicationException( + "Instance ${instance.instanceId}: Sending failed with $sendResult" + ) + } + + is ScheduleNewAssetMessageResult.Failure.Generic -> { + if (result.coreFailure is StorageFailure.Generic) { + val rootCause = (result.coreFailure as StorageFailure.Generic).rootCause.message + throw WebApplicationException( + "Instance ${instance.instanceId}: Sending failed with $rootCause" + ) + } else { + throw WebApplicationException("Instance ${instance.instanceId}: Sending failed") + } + } } } @@ -572,7 +584,7 @@ sealed class ConversationRepository { } } - @Suppress("LongParameterList") + @Suppress("LongParameterList", "ThrowsCount") suspend fun sendImage( instance: Instance, conversationId: ConversationId, @@ -608,17 +620,24 @@ sealed class ConversationRepository { height, 0L ) - if (sendResult is ScheduleNewAssetMessageResult.Failure) { - if (sendResult.coreFailure is StorageFailure.Generic) { - val rootCause = (sendResult.coreFailure as StorageFailure.Generic).rootCause.message - throw WebApplicationException( - "Instance ${instance.instanceId}: Sending failed with $rootCause" - ) - } else { - throw WebApplicationException("Instance ${instance.instanceId}: Sending failed") + when (sendResult) { + ScheduleNewAssetMessageResult.Failure.RestrictedFileType, + ScheduleNewAssetMessageResult.Failure.DisabledByTeam -> { + throw WebApplicationException("Instance ${instance.instanceId}: Sending failed with $sendResult") } - } else { - Response.status(Response.Status.OK).build() + + is ScheduleNewAssetMessageResult.Failure.Generic -> { + if (sendResult.coreFailure is StorageFailure.Generic) { + val rootCause = (sendResult.coreFailure as StorageFailure.Generic).rootCause.message + throw WebApplicationException( + "Instance ${instance.instanceId}: Sending failed with $rootCause" + ) + } else { + throw WebApplicationException("Instance ${instance.instanceId}: Sending failed") + } + } + + is ScheduleNewAssetMessageResult.Success -> Response.status(Response.Status.OK).build() } } }