From 8f6edba40364cd923f700c2e10367e1bc8d5e87f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 21 Aug 2023 18:00:41 +0200 Subject: [PATCH 1/8] Fix typo --- .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 2 +- .../matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index bbef75a21d..e852c61185 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -298,7 +298,7 @@ internal class DefaultAuthenticationService @Inject constructor( } // If an m.login.sso flow is present that is flagged as being for MSC3824 OIDC compatibility then we only return that flow - val oidcCompatibilityFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.sso" && it.delegatedOidcCompatibilty == true } + val oidcCompatibilityFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.sso" && it.delegatedOidcCompatibility == true } val flows = if (oidcCompatibilityFlow != null) listOf(oidcCompatibilityFlow) else loginFlowResponse.flows val supportsGetLoginTokenFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.token" && it.getLoginToken == true } != null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt index ea749a56b8..2e52740ed4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt @@ -51,7 +51,7 @@ internal data class LoginFlow( * See [MSC3824](https://github.com/matrix-org/matrix-spec-proposals/pull/3824) */ @Json(name = "org.matrix.msc3824.delegated_oidc_compatibility") - val delegatedOidcCompatibilty: Boolean? = null, + val delegatedOidcCompatibility: Boolean? = null, /** * Whether a login flow of type m.login.token could accept a token issued using /login/get_token. From 12395e9b04c24f92583c101774d8e60cd9d1894f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 22 Aug 2023 11:40:47 +0200 Subject: [PATCH 2/8] OIDC redirect to the web page to delete a session (legacy session manager) #8616 --- .../features/settings/devices/DevicesViewEvents.kt | 2 ++ .../features/settings/devices/DevicesViewModel.kt | 14 ++++++++++++++ .../devices/VectorSettingsDevicesFragment.kt | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt index c97f353110..de9671e360 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt @@ -50,4 +50,6 @@ sealed class DevicesViewEvents : VectorViewEvents { data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvents() object PromptResetSecrets : DevicesViewEvents() + + data class OpenBrowser(val url: String) : DevicesViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 526a6006e1..5a1e602808 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -346,6 +346,20 @@ class DevicesViewModel @AssistedInject constructor( private fun handleDelete(action: DevicesAction.Delete) { val deviceId = action.deviceId + val accountManagementUrl = session.homeServerCapabilitiesService().getHomeServerCapabilities().externalAccountManagementUrl + if (accountManagementUrl != null) { + // Open external browser to delete this session + _viewEvents.post( + DevicesViewEvents.OpenBrowser( + url = accountManagementUrl.removeSuffix("/") + "?action=session_end&device_id=$deviceId" + ) + ) + } else { + doDelete(deviceId) + } + } + + private fun doDelete(deviceId: String) { setState { copy( request = Loading() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt index 5e2f0b21f2..9cb314b2b7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -35,6 +35,7 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.auth.ReAuthActivity @@ -95,6 +96,9 @@ class VectorSettingsDevicesFragment : is DevicesViewEvents.PromptResetSecrets -> { navigator.open4SSetup(requireActivity(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET) } + is DevicesViewEvents.OpenBrowser -> { + openUrlInChromeCustomTab(requireContext(), null, it.url) + } } } } From 425441546e5d751fb4baba21cd1807d8b2157e67 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 22 Aug 2023 12:04:52 +0200 Subject: [PATCH 3/8] Format --- .../devices/v2/othersessions/OtherSessionsFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt index cee90986cd..78075ca185 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt @@ -308,7 +308,10 @@ class OtherSessionsFragment : ) ) views.otherSessionsNotFoundTextView.text = getString(R.string.device_manager_other_sessions_no_inactive_sessions_found) - updateSecurityLearnMoreButton(R.string.device_manager_learn_more_sessions_inactive_title, R.string.device_manager_learn_more_sessions_inactive) + updateSecurityLearnMoreButton( + R.string.device_manager_learn_more_sessions_inactive_title, + R.string.device_manager_learn_more_sessions_inactive + ) } DeviceManagerFilterType.ALL_SESSIONS -> { /* NOOP. View is not visible */ } From 8941e6396c555b0fa17230ad4629356875d04837 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 22 Aug 2023 12:04:38 +0200 Subject: [PATCH 4/8] Hide multi signout if we have an external account manager (#8616) --- .../settings/devices/v2/DevicesViewModel.kt | 14 +++++++++++++- .../settings/devices/v2/DevicesViewState.kt | 1 + .../v2/VectorSettingsDevicesFragment.kt | 18 +++++++++++------- .../v2/othersessions/OtherSessionsFragment.kt | 11 ++++++++--- .../v2/othersessions/OtherSessionsViewModel.kt | 14 +++++++++++++- .../v2/othersessions/OtherSessionsViewState.kt | 1 + 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index f2e6b25f32..369150d45e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -40,7 +40,7 @@ import timber.log.Timber class DevicesViewModel @AssistedInject constructor( @Assisted initialState: DevicesViewState, - activeSessionHolder: ActiveSessionHolder, + private val activeSessionHolder: ActiveSessionHolder, private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase, private val refreshDevicesOnCryptoDevicesChangeUseCase: RefreshDevicesOnCryptoDevicesChangeUseCase, @@ -69,6 +69,18 @@ class DevicesViewModel @AssistedInject constructor( refreshDeviceList() refreshIpAddressVisibility() observePreferences() + initExternalAccountManagementUrl() + } + + private fun initExternalAccountManagementUrl() { + setState { + copy( + externalAccountManagementUrl = activeSessionHolder.getSafeActiveSession() + ?.homeServerCapabilitiesService() + ?.getHomeServerCapabilities() + ?.externalAccountManagementUrl + ) + } } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt index 75d0f132bb..863ecd17a3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt @@ -26,6 +26,7 @@ data class DevicesViewState( val devices: Async = Uninitialized, val isLoading: Boolean = false, val isShowingIpAddress: Boolean = false, + val externalAccountManagementUrl: String? = null, ) : MavericksState data class DeviceFullInfoList( diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index ec21970f94..2850064609 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -290,8 +290,8 @@ class VectorSettingsDevicesFragment : val unverifiedSessionsCount = deviceFullInfoList?.unverifiedSessionsCount ?: 0 renderSecurityRecommendations(inactiveSessionsCount, unverifiedSessionsCount) - renderCurrentSessionView(currentDeviceInfo, hasOtherDevices = otherDevices?.isNotEmpty().orFalse()) - renderOtherSessionsView(otherDevices, state.isShowingIpAddress) + renderCurrentSessionView(currentDeviceInfo, hasOtherDevices = otherDevices?.isNotEmpty().orFalse(), state) + renderOtherSessionsView(otherDevices, state) } else { hideSecurityRecommendations() hideCurrentSessionView() @@ -347,13 +347,16 @@ class VectorSettingsDevicesFragment : hideInactiveSessionsRecommendation() } - private fun renderOtherSessionsView(otherDevices: List?, isShowingIpAddress: Boolean) { + private fun renderOtherSessionsView(otherDevices: List?, state: DevicesViewState) { + val isShowingIpAddress = state.isShowingIpAddress if (otherDevices.isNullOrEmpty()) { hideOtherSessionsView() } else { views.deviceListHeaderOtherSessions.isVisible = true val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) val multiSignoutItem = views.deviceListHeaderOtherSessions.menu.findItem(R.id.otherSessionsHeaderMultiSignout) + // Hide multi signout if we have an external account manager + multiSignoutItem.isVisible = state.externalAccountManagementUrl == null val nbDevices = otherDevices.size multiSignoutItem.title = stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_multi_signout_all, nbDevices, nbDevices) multiSignoutItem.setTextColor(colorDestructive) @@ -377,23 +380,24 @@ class VectorSettingsDevicesFragment : views.deviceListOtherSessions.isVisible = false } - private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?, hasOtherDevices: Boolean) { + private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?, hasOtherDevices: Boolean, state: DevicesViewState) { currentDeviceInfo?.let { - renderCurrentSessionHeaderView(hasOtherDevices) + renderCurrentSessionHeaderView(hasOtherDevices, state) renderCurrentSessionListView(it) } ?: run { hideCurrentSessionView() } } - private fun renderCurrentSessionHeaderView(hasOtherDevices: Boolean) { + private fun renderCurrentSessionHeaderView(hasOtherDevices: Boolean, state: DevicesViewState) { views.deviceListHeaderCurrentSession.isVisible = true val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) val signoutSessionItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignout) signoutSessionItem.setTextColor(colorDestructive) val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) signoutOtherSessionsItem.setTextColor(colorDestructive) - signoutOtherSessionsItem.isVisible = hasOtherDevices + // Hide signout other sessions if we have an external account manager + signoutOtherSessionsItem.isVisible = hasOtherDevices && state.externalAccountManagementUrl == null } private fun renderCurrentSessionListView(currentDeviceInfo: DeviceFullInfo) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt index 78075ca185..f935cb83ab 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt @@ -103,10 +103,15 @@ class OtherSessionsFragment : val nbDevices = viewState.devices()?.size ?: 0 stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_multi_signout_all, nbDevices, nbDevices) } - multiSignoutItem.isVisible = if (viewState.isSelectModeEnabled) { - viewState.devices.invoke()?.any { it.isSelected }.orFalse() + multiSignoutItem.isVisible = if (viewState.externalAccountManagementUrl != null) { + // Hide multi signout if we have an external account manager + false } else { - viewState.devices.invoke()?.isNotEmpty().orFalse() + if (viewState.isSelectModeEnabled) { + viewState.devices.invoke()?.any { it.isSelected }.orFalse() + } else { + viewState.devices.invoke()?.isNotEmpty().orFalse() + } } val showAsActionFlag = if (viewState.isSelectModeEnabled) MenuItem.SHOW_AS_ACTION_IF_ROOM else MenuItem.SHOW_AS_ACTION_NEVER multiSignoutItem.setShowAsAction(showAsActionFlag or MenuItem.SHOW_AS_ACTION_WITH_TEXT) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt index a5282e7ba2..3d41850027 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt @@ -41,7 +41,7 @@ import timber.log.Timber class OtherSessionsViewModel @AssistedInject constructor( @Assisted private val initialState: OtherSessionsViewState, - activeSessionHolder: ActiveSessionHolder, + private val activeSessionHolder: ActiveSessionHolder, private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase, private val signoutSessionsUseCase: SignoutSessionsUseCase, private val pendingAuthHandler: PendingAuthHandler, @@ -65,6 +65,18 @@ class OtherSessionsViewModel @AssistedInject constructor( observeDevices(initialState.currentFilter) refreshIpAddressVisibility() observePreferences() + initExternalAccountManagementUrl() + } + + private fun initExternalAccountManagementUrl() { + setState { + copy( + externalAccountManagementUrl = activeSessionHolder.getSafeActiveSession() + ?.homeServerCapabilitiesService() + ?.getHomeServerCapabilities() + ?.externalAccountManagementUrl + ) + } } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt index f4dd3640ee..ccb3e57f41 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt @@ -29,6 +29,7 @@ data class OtherSessionsViewState( val isSelectModeEnabled: Boolean = false, val isLoading: Boolean = false, val isShowingIpAddress: Boolean = false, + val externalAccountManagementUrl: String? = null, ) : MavericksState { constructor(args: OtherSessionsArgs) : this(excludeCurrentDevice = args.excludeCurrentDevice) From 880ed69f973460628817a4ddd951851b5d70fd49 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 22 Aug 2023 12:17:23 +0200 Subject: [PATCH 5/8] OIDC redirect to the web page to delete a session (new session manager) #8616 --- .../v2/overview/SessionOverviewFragment.kt | 18 ++++++++++++++---- .../v2/overview/SessionOverviewViewModel.kt | 12 ++++++++++++ .../v2/overview/SessionOverviewViewState.kt | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index 399f99201b..7b10939072 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -39,6 +39,7 @@ import im.vector.app.core.platform.VectorMenuProvider import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentSessionOverviewBinding import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.recover.SetupMode @@ -135,10 +136,19 @@ class SessionOverviewFragment : activity?.let { SignOutUiWorker(it).perform() } } - private fun confirmSignoutOtherSession() { - activity?.let { - buildConfirmSignoutDialogUseCase.execute(it, this::signoutSession) - .show() + private fun confirmSignoutOtherSession() = withState(viewModel) { state -> + if (state.externalAccountManagementUrl != null) { + // Manage in external account manager + openUrlInChromeCustomTab( + requireContext(), + null, + state.externalAccountManagementUrl.removeSuffix("/") + "?action=session_end&device_id=${state.deviceId}" + ) + } else { + activity?.let { + buildConfirmSignoutDialogUseCase.execute(it, this::signoutSession) + .show() + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index 55866cb8c4..80db4717f0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -75,6 +75,18 @@ class SessionOverviewViewModel @AssistedInject constructor( observeNotificationsStatus(initialState.deviceId) refreshIpAddressVisibility() observePreferences() + initExternalAccountManagementUrl() + } + + private fun initExternalAccountManagementUrl() { + setState { + copy( + externalAccountManagementUrl = activeSessionHolder.getSafeActiveSession() + ?.homeServerCapabilitiesService() + ?.getHomeServerCapabilities() + ?.externalAccountManagementUrl + ) + } } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt index 0f66605f98..f6a1307a5f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt @@ -29,6 +29,7 @@ data class SessionOverviewViewState( val isLoading: Boolean = false, val notificationsStatus: NotificationsStatus = NotificationsStatus.NOT_SUPPORTED, val isShowingIpAddress: Boolean = false, + val externalAccountManagementUrl: String? = null, ) : MavericksState { constructor(args: SessionOverviewArgs) : this( deviceId = args.deviceId From dc19380fbffd0c1cf837bdc275633c51770ccb3f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 22 Aug 2023 12:41:13 +0200 Subject: [PATCH 6/8] Changelog --- changelog.d/8616.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/8616.misc diff --git a/changelog.d/8616.misc b/changelog.d/8616.misc new file mode 100644 index 0000000000..58a2596287 --- /dev/null +++ b/changelog.d/8616.misc @@ -0,0 +1 @@ +If an external account manager is configured on the server, use it to delete other sessions and hide the multi session deletion. From a889d8d678990521d6941b15116492785c1bf78a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 31 Aug 2023 09:57:47 +0200 Subject: [PATCH 7/8] Store the authentication issuer into DB. --- .../homeserver/HomeServerCapabilities.kt | 5 ++++ .../database/RealmSessionStoreMigration.kt | 4 ++- .../mapper/HomeServerCapabilitiesMapper.kt | 1 + .../database/migration/MigrateSessionTo053.kt | 30 +++++++++++++++++++ .../model/HomeServerCapabilitiesEntity.kt | 1 + .../GetHomeServerCapabilitiesTask.kt | 1 + 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo053.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt index 78672adb00..b7d4ea3cdc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -85,6 +85,11 @@ data class HomeServerCapabilities( * External account management url for use with MSC3824 delegated OIDC, provided in Wellknown. */ val externalAccountManagementUrl: String? = null, + + /** + * Authentication issuer for use with MSC3824 delegated OIDC, provided in Wellknown. + */ + val authenticationIssuer: String? = null, ) { enum class RoomCapabilitySupport { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index bca189134b..4376323df4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -69,6 +69,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo049 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo050 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo051 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo052 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo053 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -77,7 +78,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 52L, + schemaVersion = 53L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -139,5 +140,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 50) MigrateSessionTo050(realm).perform() if (oldVersion < 51) MigrateSessionTo051(realm).perform() if (oldVersion < 52) MigrateSessionTo052(realm).perform() + if (oldVersion < 53) MigrateSessionTo053(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt index b566cf7cb6..f5cf88e2c1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt @@ -49,6 +49,7 @@ internal object HomeServerCapabilitiesMapper { canRemotelyTogglePushNotificationsOfDevices = entity.canRemotelyTogglePushNotificationsOfDevices, canRedactRelatedEvents = entity.canRedactEventWithRelations, externalAccountManagementUrl = entity.externalAccountManagementUrl, + authenticationIssuer = entity.authenticationIssuer, ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo053.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo053.kt new file mode 100644 index 0000000000..32fac1ad4b --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo053.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields +import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo053(realm: DynamicRealm) : RealmMigrator(realm, 53) { + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("HomeServerCapabilitiesEntity") + ?.addField(HomeServerCapabilitiesEntityFields.AUTHENTICATION_ISSUER, String::class.java) + ?.forceRefreshOfHomeServerCapabilities() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt index 35a5c654de..79aaf6d4ce 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt @@ -36,6 +36,7 @@ internal open class HomeServerCapabilitiesEntity( var canRemotelyTogglePushNotificationsOfDevices: Boolean = false, var canRedactEventWithRelations: Boolean = false, var externalAccountManagementUrl: String? = null, + var authenticationIssuer: String? = null, ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 71f6ebd6e3..b973de9fd3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -165,6 +165,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( Timber.v("Extracted integration config : $config") realm.insertOrUpdate(config) } + homeServerCapabilitiesEntity.authenticationIssuer = getWellknownResult.wellKnown.unstableDelegatedAuthConfig?.issuer homeServerCapabilitiesEntity.externalAccountManagementUrl = getWellknownResult.wellKnown.unstableDelegatedAuthConfig?.accountManagementUrl } From 52a06931f47d6f86d0e54fe1ff31859486c75e1d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 31 Aug 2023 15:51:18 +0200 Subject: [PATCH 8/8] Change the test to hide multi signout of devices. We do not need an external account management URL, which is optional, but we need to know if account management is delegate to Oidc. --- .../api/session/homeserver/HomeServerCapabilities.kt | 2 ++ .../features/settings/devices/v2/DevicesViewModel.kt | 10 ++++++---- .../features/settings/devices/v2/DevicesViewState.kt | 2 +- .../devices/v2/VectorSettingsDevicesFragment.kt | 8 ++++---- .../devices/v2/othersessions/OtherSessionsFragment.kt | 4 ++-- .../devices/v2/othersessions/OtherSessionsViewModel.kt | 10 ++++++---- .../devices/v2/othersessions/OtherSessionsViewState.kt | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt index b7d4ea3cdc..6b94452e39 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -146,6 +146,8 @@ data class HomeServerCapabilities( return cap?.preferred ?: cap?.support?.lastOrNull() } + val delegatedOidcAuthEnabled: Boolean = authenticationIssuer != null + companion object { const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L const val ROOM_CAP_KNOCK = "knock" diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index 369150d45e..43530cb17c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import timber.log.Timber @@ -69,16 +70,17 @@ class DevicesViewModel @AssistedInject constructor( refreshDeviceList() refreshIpAddressVisibility() observePreferences() - initExternalAccountManagementUrl() + initDelegatedOidcAuthEnabled() } - private fun initExternalAccountManagementUrl() { + private fun initDelegatedOidcAuthEnabled() { setState { copy( - externalAccountManagementUrl = activeSessionHolder.getSafeActiveSession() + delegatedOidcAuthEnabled = activeSessionHolder.getSafeActiveSession() ?.homeServerCapabilitiesService() ?.getHomeServerCapabilities() - ?.externalAccountManagementUrl + ?.delegatedOidcAuthEnabled + .orFalse() ) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt index 863ecd17a3..6d6baa9d5f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt @@ -26,7 +26,7 @@ data class DevicesViewState( val devices: Async = Uninitialized, val isLoading: Boolean = false, val isShowingIpAddress: Boolean = false, - val externalAccountManagementUrl: String? = null, + val delegatedOidcAuthEnabled: Boolean = false, ) : MavericksState data class DeviceFullInfoList( diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index 2850064609..bf90120b96 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -355,8 +355,8 @@ class VectorSettingsDevicesFragment : views.deviceListHeaderOtherSessions.isVisible = true val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) val multiSignoutItem = views.deviceListHeaderOtherSessions.menu.findItem(R.id.otherSessionsHeaderMultiSignout) - // Hide multi signout if we have an external account manager - multiSignoutItem.isVisible = state.externalAccountManagementUrl == null + // Hide multi signout if the homeserver delegates the account management + multiSignoutItem.isVisible = state.delegatedOidcAuthEnabled.not() val nbDevices = otherDevices.size multiSignoutItem.title = stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_multi_signout_all, nbDevices, nbDevices) multiSignoutItem.setTextColor(colorDestructive) @@ -396,8 +396,8 @@ class VectorSettingsDevicesFragment : signoutSessionItem.setTextColor(colorDestructive) val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) signoutOtherSessionsItem.setTextColor(colorDestructive) - // Hide signout other sessions if we have an external account manager - signoutOtherSessionsItem.isVisible = hasOtherDevices && state.externalAccountManagementUrl == null + // Hide signout other sessions if the homeserver delegates the account management + signoutOtherSessionsItem.isVisible = hasOtherDevices && state.delegatedOidcAuthEnabled.not() } private fun renderCurrentSessionListView(currentDeviceInfo: DeviceFullInfo) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt index f935cb83ab..a5897be58c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt @@ -103,8 +103,8 @@ class OtherSessionsFragment : val nbDevices = viewState.devices()?.size ?: 0 stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_multi_signout_all, nbDevices, nbDevices) } - multiSignoutItem.isVisible = if (viewState.externalAccountManagementUrl != null) { - // Hide multi signout if we have an external account manager + multiSignoutItem.isVisible = if (viewState.delegatedOidcAuthEnabled) { + // Hide multi signout if the homeserver delegates the account management false } else { if (viewState.isSelectModeEnabled) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt index 3d41850027..fd463a933c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt @@ -36,6 +36,7 @@ import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthN import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import timber.log.Timber @@ -65,16 +66,17 @@ class OtherSessionsViewModel @AssistedInject constructor( observeDevices(initialState.currentFilter) refreshIpAddressVisibility() observePreferences() - initExternalAccountManagementUrl() + initDelegatedOidcAuthEnabled() } - private fun initExternalAccountManagementUrl() { + private fun initDelegatedOidcAuthEnabled() { setState { copy( - externalAccountManagementUrl = activeSessionHolder.getSafeActiveSession() + delegatedOidcAuthEnabled = activeSessionHolder.getSafeActiveSession() ?.homeServerCapabilitiesService() ?.getHomeServerCapabilities() - ?.externalAccountManagementUrl + ?.delegatedOidcAuthEnabled + .orFalse() ) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt index ccb3e57f41..dcff652768 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt @@ -29,7 +29,7 @@ data class OtherSessionsViewState( val isSelectModeEnabled: Boolean = false, val isLoading: Boolean = false, val isShowingIpAddress: Boolean = false, - val externalAccountManagementUrl: String? = null, + val delegatedOidcAuthEnabled: Boolean = false, ) : MavericksState { constructor(args: OtherSessionsArgs) : this(excludeCurrentDevice = args.excludeCurrentDevice)