Skip to content

Commit

Permalink
feat: mls (epic) (#2015)
Browse files Browse the repository at this point in the history
* feat(mls-migration): fetch migration configuration every 24 hours #1 (#1728)

* feat(mls-migration): migrate proteus conversations to "mixed" protocol  #2 (#1729)

* feat: add api support for updating the conversation protocol

* fix: rollback when we lost the race to establish a MLS group

* feat: add method for migrating from proteus to mixed

* feat: allow controlling migration update interval in CLI

* fix: use correct use case for checking if we can register an MLS client

* chore: add debug response since BE is not implemented yet

* test: add test for aborting when losing the race to establish an mls group

* test: add network test for updating protocol

* test: add MLSMigrationManager tests

* test: add MLSMigrator tests

* chore: fix detekt

* chore: make method private

* refactor: add sql query for fetching proteus team conversations

* feat(mls-migration): support the mixed protocol #3 (#1747)

* feat(mls-migration): supported protocols for users #4 (#1786)

* feat: add API support for supported-protocols

* feat: support persisting supported protocols

* feat: add mapping of supported protocols in logic module

* chore: fix detekt

* refactor: rename UserProtocol to SupportedProtocolDTO

* chore: move BaseApi interface to its own file

* chore: fix imports after rebase

* fix: persist supported protocols locally when API request succeeds

* chore: remove debug line

* fix: mapsupported protocols when fetching one-to-one conversationd details

* refactor: move default value for supportedProtocols to the logic mapping layer

* feat(mls-migration): add use case for updating self supported protocols #5 (#1797)

* feat: add use case for calculating & updating your supported protocols

* test: add UpdateSupportedProtocolsUseCase tests

* refactor: simplify UpdateSupportedProtocolsUseCase

* test: update feature config test data

* chore: add mlsPublicKeys to NewClientDTO

* feat: persist if a self client is mls capable or not

* feat: update supported protocols from user.update events

* fix: migration (altering column positions not supported)

* chore: clean up disabled migration configuration

* feat(mls-migration): update conversation protocol to MLS when every member supports MLS #6 (#1802)

* feat: support fetching conversation which can be finalised

* feat: add function for finalising migration of team group conversations

* refactor: move methods for updating conversation protocol to the conversation repository

* chore: fix copy & paste error

* refactor: don't return a flow when fetching conversations for migration

* chore: rebase clean up

* performance: replace string matching by listing all cases

* feat(mls-migration): handle protocol changed events #7 (#1811)

* feat: support persisting protocol changed system messages

* feat: process protocol change events and insert system messages

* feat: insert history lost system message if protocol change is discovered during slow sync

* chore: add migration of new system message table

* fix: check if protocol was updated inside sql query

* fix: only fetch group conversations by protocol

* chore: document return value

* chore: fix test naming

* refactor: rename SystemMessageBuilder to SystemMessageInserter

* chore: fix logging inconsistency

* fix: import

* chore: fix compilation

* fix(mls-migration): migration feature config #8 (#1822)

* fix: mls migration config fields are all optional

* chore: update tests after removing no longer necessary fields

* chore: fix detekt

* feat(mls-migration): update self supported protocols during slow sync (#1826) #9

* feat(mls-migration): force migration when migration deadline arrives #10 (#1831)

* feat: end migration regardless when migration deadline arrives

* refactor: generalise methods for fetching conversations ids

* refactor: better naming

* feat(mls-migration): ignore inactive clients when calculating supported protocols #11 (#1904)

* test: add test cases for ignoring inactive clients when calculating supported protocols

* feat: add isActive computed property on Client model

* feat: ignore inactive clients when checking if all self clients are mls capable

* fix: updating isMLSCapable (#1905)

* fix: updating isMLSCapable

It should be possible to update isMlsCapable from false to true but not the other
way around.

* chore: add comment explaining update logic for is_mls_capable

* chore: fix detekt

* fix: fetch  group id on protocol change event (#2051)

* chore: fix rebase issues

* feat(mls-one2one): support fetching mls 1-1 conversation details (#1848)

* feat: support fetching mls 1-1 conversation details

* refactor: better naming of function

* chore: remove another magic number

* feat: support migrating to mls 1-1 (#1875)

* refactor: allow fetching whole conversations by a member (#1940)

* feat(mls): establish one-to-one conversation [WPB-2258] (#1953)

* feat(mls): migrate 1:1 connections from Proteus to MLS [WPB-2258] (#1968)

* feat: choose 1:1 protocol [WPB-2181] (#1993)

Co-authored-by: Mojtaba Chenani <[email protected]>

* feat: check supported protocols when creating/fetching team 1-1 (#1995)

* feat: establish mls 1-1 for team members if mls is the default protocol

* refactor: update GetOrCreateOneToOneConversationUseCaseTest to use the arrangement pattern

* test: update with test cases covering mls as the default protocol

* feat: support persisting the default protocol from mls feature config

* test: update & add tests for persisting the default protocol

* chore: EstablishMLSOneToOneUseCase was renamed into MLSOneOnOneConversationResolver

* test: don't expect team 1-1 to be established

* fix: use protocol selector for deciding which protocol use for team 1-1

* chore: fix detekt

* feat: active one-on-one conversations (#2010)

* feat(persistance): active/inactive one-on-one conversations

* fix: more efficient mapping

Co-authored-by: Mojtaba Chenani <[email protected]>

* refactor: join with Conversation table instead

Co-authored-by: Mojtaba Chenani <[email protected]>

* refactor: rename oneOnOneConversationId to activeOneOnOneconversationId

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

---------

Co-authored-by: Mojtaba Chenani <[email protected]>
Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* feat: resolve one-on-one conversation to either proteus or mls (#2012)

* chore: default selfUser when creator field is missing (#2022)

* feat: resolve one-on-one conversaions when receiving a welcome message (#2033)

* feat: resolve one-on-on conversation when the connection is accepted (#2034)

* feat: resolve active 1-1 after accepting a connection request (#2042)

* feat: support parsing quantum safe ciphersuite (#2049)

* feat: avoid race condition when establishing mls 1-1 (#2048)

* feat: add live property to Event model

 Distinguish between live events arriving via the websocket vs events fetched when catching up when connectivity is restored.

* feat: delay resolving active one-on-one when live

Delay resolving active one-on-one when a connection request is accepted and we are live. This avoids a race to establish the mls group when multiple clients are online, which is wasteful.

* chore: update tests after adding live propperty

* chore: fix detekt

* fix: always schedule resolving active 1-1 to avoid discarding welcome msg

* fix: improve stability of MLS 1-1 conversations (#2063)

* fix: don't fail migration query if messages has already been copied

* fix: don't fail the slow sync on a non-recoverable error when resolving 1-1s

* fix: don't fail the slow sync when etablishing 1-1 fails due to missing key packages

* fix: re-use existing mls group if it exists

* fix: establish 1-1 also with other self clients

* test: add missing test for establishing 1-1

* chore: fix detekt

* feat: limit re-trying commit to 2 retry attemps (#2068)

* feat: limit re-trying commit to 2 retry attemps

* refactor: rename retryCount to remainingAttempts for better readability

* chore: fix rebase issues

* fix: map and ignore BufferedFutureMessage (#2084)

* feat: update supported protocols after deleting a client (#2083)

* feat(mls): recover from stale epoch on message sending (#2076)

* feat: parse epoch timestamp in conversation response

* feat: persist epoch timestamp

* feat: verify if we have lost commits when sending fails

* test: add tests for stale epoch handler

* fix: don't compare against  last processed event instead use time heuristic

* fix: verify epoch using CC in WrongEpochHandler

* refactor: replace MLSWrongEpochHandler with StaleEpochVerifier since they are identical

* fix: fail re-try if verifying epoch fails

* Revert "feat: persist epoch timestamp"

This reverts commit e968af6.

* Revert "feat: parse epoch timestamp in conversation response"

This reverts commit f1bf8b4.

* chore: remove any remaining trace of epochTimestamp

* chore: fix rebase issues

* feat(debug): add use case for disabling event processing (#2096)

* chore: fix rebase issues

* refactor: mls feature config handling (#2114)

* refactor: mls feature config handling

* chore: remove debug println

* chore: use named arguments for better readability

* refactor: extract handlers into variables and re-use them

* fix: assign supported_protocols when migration user db (#2126)

* chore(mls): propagate activeOneOnOneConversation on ConversationDetails [WPB-4705] (#2130)

* refactor: only have one main query for inserting/updating users (#2124)

* refactor: only have one main query for inserting/updating users

* fix: incorrect user mapping for team member

* chore: inline function which is only used once

* fix: updating connection status

* fix: user type not updating for non-team contacts

* fix: typo in partial user update query

* chore: update tests after rebase

* fix: supported protocols is only available since API v5 (#2128)

* fix: skip updating supported protocols when mls is not supported (#2131)

* chore: fix rebase issues

* chore: squash migrations when possible

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>
Co-authored-by: Mojtaba Chenani <[email protected]>
  • Loading branch information
3 people authored Oct 15, 2023
1 parent 6ee492b commit 4d801fc
Show file tree
Hide file tree
Showing 270 changed files with 10,552 additions and 2,855 deletions.
4 changes: 3 additions & 1 deletion cli/src/appleMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.wire.kalium.cli.commands.MarkAsReadCommand
import com.wire.kalium.cli.commands.RefillKeyPackagesCommand
import com.wire.kalium.cli.commands.RemoveMemberFromGroupCommand
import com.wire.kalium.cli.commands.InteractiveCommand
import com.wire.kalium.cli.commands.UpdateSupportedProtocolsCommand

fun main(args: Array<String>) = CLIApplication().subcommands(
LoginCommand().subcommands(
Expand All @@ -37,6 +38,7 @@ fun main(args: Array<String>) = CLIApplication().subcommands(
RemoveMemberFromGroupCommand(),
RefillKeyPackagesCommand(),
MarkAsReadCommand(),
InteractiveCommand()
InteractiveCommand(),
UpdateSupportedProtocolsCommand()
)
).main(args)
24 changes: 18 additions & 6 deletions cli/src/commonMain/kotlin/com/wire/kalium/cli/CLIApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,25 @@ import com.wire.kalium.logic.CoreLogger
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.featureFlags.KaliumConfigs
import kotlinx.coroutines.runBlocking
import kotlin.time.Duration

class CLIApplication : CliktCommand(allowMultipleSubcommands = true) {

private val logLevel by option(help = "log level").enum<KaliumLogLevel>().default(KaliumLogLevel.WARN)
private val logOutputFile by option(help = "output file for logs")
private val developmentApiEnabled by option(help = "use development API if supported by backend").flag(default = false)
private val encryptProteusStorage by option(help = "use encrypted storage for proteus sessions and identity").flag(default = false)
private val logLevel by option(
help = "log level"
).enum<KaliumLogLevel>().default(KaliumLogLevel.WARN)
private val logOutputFile by option(
help = "output file for logs"
)
private val developmentApiEnabled by option(
help = "use development API if supported by backend"
).flag(default = false)
private val encryptProteusStorage by option(
help = "use encrypted storage for proteus sessions and identity"
).flag(default = false)
private val mlsMigrationInterval by option(
help = "interval at which mls migration is updated"
).default("24h")
private val fileLogger: LogWriter by lazy { fileLogger(logOutputFile ?: "kalium.log") }

override fun run() = runBlocking {
Expand All @@ -45,7 +57,8 @@ class CLIApplication : CliktCommand(allowMultipleSubcommands = true) {
rootPath = "$HOME_DIRECTORY/.kalium/accounts",
kaliumConfigs = KaliumConfigs(
developmentApiEnabled = developmentApiEnabled,
encryptProteusStorage = encryptProteusStorage
encryptProteusStorage = encryptProteusStorage,
mlsMigrationInterval = Duration.parse(mlsMigrationInterval)
)
)
}
Expand All @@ -63,7 +76,6 @@ class CLIApplication : CliktCommand(allowMultipleSubcommands = true) {
companion object {
val HOME_DIRECTORY: String = homeDirectory()
}

}

expect fun fileLogger(filePath: String): LogWriter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/

package com.wire.kalium.cli.commands

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.PrintMessage
import com.github.ajalt.clikt.core.requireObject
import com.wire.kalium.logic.feature.UserSessionScope
import com.wire.kalium.logic.functional.fold
import kotlinx.coroutines.runBlocking

class UpdateSupportedProtocolsCommand : CliktCommand(name = "update-supported-protocols") {

private val userSession by requireObject<UserSessionScope>()

override fun run() = runBlocking {
userSession.syncManager.waitUntilLive()
userSession.users.updateSupportedProtocols().fold({ failure ->
throw PrintMessage("updating supported protocols failed: $failure")
}, {
echo("supported protocols were updated")
})
}
}
4 changes: 3 additions & 1 deletion cli/src/jvmMain/kotlin/com/wire/kalium/cli/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.wire.kalium.cli.commands.MarkAsReadCommand
import com.wire.kalium.cli.commands.ConsoleCommand
import com.wire.kalium.cli.commands.RefillKeyPackagesCommand
import com.wire.kalium.cli.commands.RemoveMemberFromGroupCommand
import com.wire.kalium.cli.commands.UpdateSupportedProtocolsCommand

fun main(args: Array<String>) = CLIApplication().subcommands(
LoginCommand().subcommands(
Expand All @@ -38,6 +39,7 @@ fun main(args: Array<String>) = CLIApplication().subcommands(
RemoveMemberFromGroupCommand(),
ConsoleCommand(),
RefillKeyPackagesCommand(),
MarkAsReadCommand()
MarkAsReadCommand(),
UpdateSupportedProtocolsCommand()
)
).main(args)
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

@IgnoreJS
@IgnoreIOS
@IgnoreJS
class E2EIClientTest : BaseMLSClientTest() {
data class SampleUser(
val id: CryptoQualifiedID, val clientId: CryptoClientId, val name: String, val handle: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ actual fun mapMLSException(exception: Exception): MLSFailure =
when (exception.error) {
is CryptoError.WrongEpoch -> MLSFailure.WrongEpoch
is CryptoError.DuplicateMessage -> MLSFailure.DuplicateMessage
is CryptoError.BufferedFutureMessage -> MLSFailure.BufferedFutureMessage
is CryptoError.SelfCommitIgnored -> MLSFailure.SelfCommitIgnored
is CryptoError.UnmergedPendingGroup -> MLSFailure.UnmergedPendingGroup
is CryptoError.ConversationAlreadyExists -> MLSFailure.ConversationAlreadyExists
else -> MLSFailure.Generic(exception)
}
} else {
Expand Down
18 changes: 18 additions & 0 deletions logic/src/commonMain/kotlin/com/wire/kalium/logic/CoreFailure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package com.wire.kalium.logic
import com.wire.kalium.cryptography.exceptions.ProteusException
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.network.exceptions.APINotSupported
import com.wire.kalium.network.exceptions.KaliumException
import com.wire.kalium.network.exceptions.isFederationDenied
import com.wire.kalium.network.utils.NetworkResponse
Expand Down Expand Up @@ -99,6 +100,11 @@ sealed interface CoreFailure {
data object SyncEventOrClientNotFound : FeatureFailure()

data object FeatureNotImplemented : FeatureFailure()
/**
* No common Protocol found in order to establish a conversation between parties.
* Could be, for example, that the desired user only supports Proteus, but we only support MLS.
*/
data object NoCommonProtocolFound : FeatureFailure()
}

sealed class NetworkFailure : CoreFailure {
Expand Down Expand Up @@ -155,6 +161,10 @@ sealed class NetworkFailure : CoreFailure {

}

/**
* Failure due to a feature not supported by the current client/backend.
*/
object FeatureNotSupported : NetworkFailure()
}

interface MLSFailure : CoreFailure {
Expand All @@ -163,10 +173,14 @@ interface MLSFailure : CoreFailure {

object DuplicateMessage : MLSFailure

object BufferedFutureMessage : MLSFailure

object SelfCommitIgnored : MLSFailure

object UnmergedPendingGroup : MLSFailure

object ConversationAlreadyExists : MLSFailure

object ConversationDoesNotSupportMLS : MLSFailure

class Generic(internal val exception: Exception) : MLSFailure {
Expand Down Expand Up @@ -233,6 +247,10 @@ internal inline fun <T : Any> wrapApiRequest(networkCall: () -> NetworkResponse<
Either.Left(NetworkFailure.NoNetworkConnection(exception))
}

exception is APINotSupported -> {
Either.Left(NetworkFailure.FeatureNotSupported)
}

else -> {
Either.Left(NetworkFailure.ServerMiscommunication(result.kException))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ package com.wire.kalium.logic.configuration

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.data.featureConfig.AppLockConfigModel
import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel
import com.wire.kalium.logic.data.featureConfig.toEntity
import com.wire.kalium.logic.data.featureConfig.toModel
import com.wire.kalium.logic.data.user.SupportedProtocol
import com.wire.kalium.logic.data.user.toDao
import com.wire.kalium.logic.data.user.toModel
import com.wire.kalium.logic.feature.selfDeletingMessages.SelfDeletionMapper.toSelfDeletionTimerEntity
import com.wire.kalium.logic.feature.selfDeletingMessages.SelfDeletionMapper.toTeamSelfDeleteTimer
import com.wire.kalium.logic.feature.selfDeletingMessages.TeamSettingsSelfDeletionStatus
Expand Down Expand Up @@ -57,6 +63,10 @@ interface UserConfigRepository {
fun observeE2EISettings(): Flow<Either<StorageFailure, E2EISettings>>
fun setE2EISettings(setting: E2EISettings): Either<StorageFailure, Unit>
fun snoozeE2EINotification(duration: Duration): Either<StorageFailure, Unit>
fun setDefaultProtocol(protocol: SupportedProtocol): Either<StorageFailure, Unit>
fun getDefaultProtocol(): Either<StorageFailure, SupportedProtocol>
suspend fun setSupportedProtocols(protocols: Set<SupportedProtocol>): Either<StorageFailure, Unit>
suspend fun getSupportedProtocols(): Either<StorageFailure, Set<SupportedProtocol>>
fun setConferenceCallingEnabled(enabled: Boolean): Either<StorageFailure, Unit>
fun isConferenceCallingEnabled(): Either<StorageFailure, Boolean>
fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either<StorageFailure, Unit>
Expand All @@ -80,6 +90,8 @@ interface UserConfigRepository {
suspend fun observeTeamSettingsSelfDeletingStatus(): Flow<Either<StorageFailure, TeamSettingsSelfDeletionStatus>>
fun observeE2EINotificationTime(): Flow<Either<StorageFailure, Instant?>>
fun setE2EINotificationTime(instant: Instant): Either<StorageFailure, Unit>
suspend fun getMigrationConfiguration(): Either<StorageFailure, MLSMigrationModel>
suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either<StorageFailure, Unit>
}

@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -186,6 +198,17 @@ class UserConfigDataSource(

private fun getE2EINotificationTimeOrNull() = wrapStorageRequest { userConfigStorage.getE2EINotificationTime() }.getOrNull()

override fun setDefaultProtocol(protocol: SupportedProtocol): Either<StorageFailure, Unit> =
wrapStorageRequest { userConfigStorage.persistDefaultProtocol(protocol.toDao()) }

override fun getDefaultProtocol(): Either<StorageFailure, SupportedProtocol> =
wrapStorageRequest { userConfigStorage.defaultProtocol().toModel() }

override suspend fun setSupportedProtocols(protocols: Set<SupportedProtocol>): Either<StorageFailure, Unit> =
wrapStorageRequest { userConfigDAO.setSupportedProtocols(protocols.toDao()) }

override suspend fun getSupportedProtocols(): Either<StorageFailure, Set<SupportedProtocol>> =
wrapStorageRequest { userConfigDAO.getSupportedProtocols()?.toModel() }
override fun setConferenceCallingEnabled(enabled: Boolean): Either<StorageFailure, Unit> =
wrapStorageRequest {
userConfigStorage.persistConferenceCalling(enabled)
Expand Down Expand Up @@ -299,4 +322,14 @@ class UserConfigDataSource(
}
}
}

override suspend fun getMigrationConfiguration(): Either<StorageFailure, MLSMigrationModel> =
wrapStorageRequest {
userConfigDAO.getMigrationConfiguration()?.toModel()
}

override suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either<StorageFailure, Unit> =
wrapStorageRequest {
userConfigDAO.setMigrationConfiguration(configuration.toEntity())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ internal class CallDataSource(
).flattenConcat()
}
} ?: Either.Left(CoreFailure.NotSupportedByProteus)
is Conversation.ProtocolInfo.Proteus -> Either.Left(CoreFailure.NotSupportedByProteus)
is Conversation.ProtocolInfo.Proteus,
is Conversation.ProtocolInfo.Mixed -> Either.Left(CoreFailure.NotSupportedByProteus)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ class CallMapperImpl(
Conversation.Type.GROUP -> {
when (conversation.protocol) {
is Conversation.ProtocolInfo.MLS -> ConversationType.ConferenceMls
is Conversation.ProtocolInfo.Proteus -> ConversationType.Conference
is Conversation.ProtocolInfo.Proteus,
is Conversation.ProtocolInfo.Mixed -> ConversationType.Conference
}
}
Conversation.Type.ONE_ON_ONE -> ConversationType.OneOnOne
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ class ClientMapper(
model = client.model,
isVerified = false,
isValid = true,
mlsPublicKeys = client.mlsPublicKeys
mlsPublicKeys = client.mlsPublicKeys,
isMLSCapable = client.mlsPublicKeys?.isNotEmpty() ?: false
)

fun fromClientEntity(clientEntity: ClientEntity): Client = with(clientEntity) {
Expand All @@ -85,7 +86,8 @@ class ClientMapper(
model = model,
isVerified = isProteusVerified,
isValid = isValid,
mlsPublicKeys = mlsPublicKeys
mlsPublicKeys = mlsPublicKeys,
isMLSCapable = isMLSCapable
)
}

Expand All @@ -100,7 +102,8 @@ class ClientMapper(
model = model,
isVerified = false,
isValid = true,
mlsPublicKeys = null
mlsPublicKeys = null,
isMLSCapable = false
)
}

Expand All @@ -116,7 +119,8 @@ class ClientMapper(
model = null,
registrationDate = null,
lastActive = null,
mlsPublicKeys = null
mlsPublicKeys = null,
isMLSCapable = false
)
}
}
Expand All @@ -132,7 +136,8 @@ class ClientMapper(
model = model,
registrationDate = Instant.parse(registrationTime),
lastActive = lastActive?.let { Instant.parse(it).coerceAtMost(Clock.System.now()) },
mlsPublicKeys = mlsPublicKeys
mlsPublicKeys = mlsPublicKeys,
isMLSCapable = mlsPublicKeys?.isNotEmpty() ?: false
)
}

Expand All @@ -147,7 +152,8 @@ class ClientMapper(
model = null,
registrationDate = null,
lastActive = null,
mlsPublicKeys = null
mlsPublicKeys = null,
isMLSCapable = false
)
}

Expand All @@ -161,7 +167,8 @@ class ClientMapper(
model = event.client.model,
registrationDate = event.client.registrationTime,
lastActive = event.client.lastActive,
mlsPublicKeys = null
mlsPublicKeys = null,
isMLSCapable = event.client.isMLSCapable
)

private fun toClientTypeDTO(clientType: ClientType): ClientTypeDTO = when (clientType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package com.wire.kalium.logic.data.client

import com.wire.kalium.cryptography.PreKeyCrypto
import com.wire.kalium.logic.data.conversation.ClientId
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Duration.Companion.days

data class RegisterClientParam(
val password: String?,
Expand Down Expand Up @@ -59,8 +61,13 @@ data class Client(
val deviceType: DeviceType?,
val label: String?,
val model: String?,
val mlsPublicKeys: Map<String, String>?
)
val mlsPublicKeys: Map<String, String>?,
val isMLSCapable: Boolean
) {
companion object {
val INACTIVE_DURATION = 28.days
}
}

enum class ClientType {
Temporary,
Expand All @@ -86,3 +93,12 @@ data class OtherUserClient(
val isValid: Boolean,
val isProteusVerified: Boolean
)

/**
* True if the client is considered to be in active use.
*
* A client is considered active if it has connected to the backend within
* the `INACTIVE_DURATION`.
*/
val Client.isActive: Boolean
get() = lastActive?.let { (Clock.System.now() - it) < Client.INACTIVE_DURATION } ?: false
Loading

0 comments on commit 4d801fc

Please sign in to comment.