AccountService is created.

This commit is contained in:
onurays 2020-03-11 23:36:04 +03:00 committed by Benoit Marty
parent f00db49bda
commit dfc8e8ec4c
15 changed files with 243 additions and 209 deletions

View file

@ -22,7 +22,6 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.LoginFlowResult
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.auth.login.LoginWizard
import im.vector.matrix.android.api.auth.password.PasswordWizard
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable
@ -90,9 +89,4 @@ interface AuthenticationService {
fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
credentials: Credentials,
callback: MatrixCallback<Session>): Cancelable
/**
* Return a PasswordWizard, to update password.
*/
fun getPasswordWizard(): PasswordWizard
}

View file

@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.failure.GlobalError
import im.vector.matrix.android.api.pushrules.PushRuleService
import im.vector.matrix.android.api.session.account.AccountService
import im.vector.matrix.android.api.session.accountdata.AccountDataService
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
@ -59,7 +60,8 @@ interface Session :
InitialSyncProgressService,
HomeServerCapabilitiesService,
SecureStorageService,
AccountDataService {
AccountDataService,
AccountService {
/**
* The params associated to the session

View file

@ -14,15 +14,20 @@
* limitations under the License.
*/
package im.vector.matrix.android.api.auth.password
package im.vector.matrix.android.api.session.account
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
interface PasswordWizard {
/**
* This interface defines methods to manage the account. It's implemented at the session level.
*/
interface AccountService {
/**
* Ask the homeserver to update the password with the provided new password.
* Ask the homeserver to change the password.
* @param password Current password.
* @param newPassword New password
*/
fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.matrix.android.internal.auth.data
package im.vector.matrix.android.api.session.account.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
* Class to pass request parameters to update the password.
*/
@JsonClass(generateAdapter = true)
internal data class UpdatePasswordParams(
internal data class ChangePasswordParams(
@Json(name = "auth")
val auth: UserPasswordAuth? = null,
@ -32,9 +32,9 @@ internal data class UpdatePasswordParams(
val newPassword: String? = null
) {
companion object {
fun create(sessionId: String, userId: String, oldPassword: String, newPassword: String): UpdatePasswordParams {
return UpdatePasswordParams(
auth = UserPasswordAuth(session = sessionId, user = userId, password = oldPassword),
fun create(userId: String, oldPassword: String, newPassword: String): ChangePasswordParams {
return ChangePasswordParams(
auth = UserPasswordAuth(user = userId, password = oldPassword),
newPassword = newPassword
)
}

View file

@ -22,7 +22,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.auth.data.RiotConfig
import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed
import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
import im.vector.matrix.android.internal.auth.registration.RegistrationParams
@ -103,10 +102,4 @@ internal interface AuthAPI {
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password")
fun resetPasswordMailConfirmed(@Body params: ResetPasswordMailConfirmed): Call<Unit>
/**
* Ask the homeserver to update the password with the provided new password.
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password")
fun updatePassword(@Body params: UpdatePasswordParams): Call<Unit>
}

View file

@ -28,7 +28,6 @@ import im.vector.matrix.android.api.auth.data.Versions
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
import im.vector.matrix.android.api.auth.data.isSupportedBySdk
import im.vector.matrix.android.api.auth.login.LoginWizard
import im.vector.matrix.android.api.auth.password.PasswordWizard
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.Session
@ -38,7 +37,6 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.matrix.android.internal.auth.data.RiotConfig
import im.vector.matrix.android.internal.auth.db.PendingSessionData
import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard
import im.vector.matrix.android.internal.auth.password.DefaultPasswordWizard
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard
import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory
@ -68,7 +66,6 @@ internal class DefaultAuthenticationService @Inject constructor(
private var currentLoginWizard: LoginWizard? = null
private var currentRegistrationWizard: RegistrationWizard? = null
private var currentPasswordWizard: PasswordWizard? = null
override fun hasAuthenticatedSessions(): Boolean {
return sessionParamsStore.getLast() != null
@ -224,22 +221,6 @@ internal class DefaultAuthenticationService @Inject constructor(
}
}
override fun getPasswordWizard(): PasswordWizard {
return currentPasswordWizard
?: let {
sessionParamsStore.getLast()?.homeServerConnectionConfig?.let {
DefaultPasswordWizard(
okHttpClient,
retrofitFactory,
coroutineDispatchers,
it
).also {
currentPasswordWizard = it
}
} ?: error("HomeServerConnectionConfig is null")
}
}
override fun cancelPendingLoginOrRegistration() {
currentLoginWizard = null
currentRegistrationWizard = null

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.auth.password
import dagger.Lazy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.password.PasswordWizard
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.auth.AuthAPI
import im.vector.matrix.android.internal.auth.data.UpdatePasswordParams
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope
import okhttp3.OkHttpClient
import timber.log.Timber
internal class DefaultPasswordWizard(
okHttpClient: Lazy<OkHttpClient>,
retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val homeServerConnectionConfig: HomeServerConnectionConfig
) : PasswordWizard {
private val authAPI = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
.create(AuthAPI::class.java)
override fun updatePassword(sessionId: String, userId: String, oldPassword: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
updatePasswordInternal(sessionId, userId, oldPassword, newPassword)
}
}
private suspend fun updatePasswordInternal(sessionId: String, userId: String, oldPassword: String, newPassword: String) {
val params = UpdatePasswordParams.create(sessionId, userId, oldPassword, newPassword)
try {
executeRequest<Unit>(null) {
apiCall = authAPI.updatePassword(params)
}
} catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError
&& throwable.httpCode == 401
/* Avoid infinite loop */
&& params.auth?.session == null) {
try {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(throwable.errorBody)
} catch (e: Exception) {
null
}?.let {
// Retry with authentication
try {
executeRequest<Unit>(null) {
apiCall = authAPI.updatePassword(
params.copy(auth = params.auth?.copy(session = it.session))
)
}
return
} catch (failure: Throwable) {
throw failure
}
}
}
throw throwable
}
}
}

View file

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.failure.GlobalError
import im.vector.matrix.android.api.pushrules.PushRuleService
import im.vector.matrix.android.api.session.InitialSyncProgressService
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.account.AccountService
import im.vector.matrix.android.api.session.accountdata.AccountDataService
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
@ -94,6 +95,7 @@ internal class DefaultSession @Inject constructor(
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
private val accountDataService: Lazy<AccountDataService>,
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
private val accountService: Lazy<AccountService>,
private val timelineEventDecryptor: TimelineEventDecryptor,
private val shieldTrustUpdater: ShieldTrustUpdater)
: Session,
@ -110,7 +112,8 @@ internal class DefaultSession @Inject constructor(
SecureStorageService by secureStorageService.get(),
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
ProfileService by profileService.get(),
AccountDataService by accountDataService.get() {
AccountDataService by accountDataService.get(),
AccountService by accountService.get() {
override val sharedSecretStorageService: SharedSecretStorageService
get() = _sharedSecretStorageService.get()

View file

@ -28,6 +28,7 @@ import im.vector.matrix.android.internal.crypto.verification.SendVerificationMes
import im.vector.matrix.android.internal.di.MatrixComponent
import im.vector.matrix.android.internal.di.SessionAssistedInjectModule
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.session.account.AccountModule
import im.vector.matrix.android.internal.session.cache.CacheModule
import im.vector.matrix.android.internal.session.content.ContentModule
import im.vector.matrix.android.internal.session.content.UploadContentWorker
@ -55,24 +56,25 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
@Component(dependencies = [MatrixComponent::class],
modules = [
SessionModule::class,
RoomModule::class,
SyncModule::class,
HomeServerCapabilitiesModule::class,
SignOutModule::class,
GroupModule::class,
UserModule::class,
FilterModule::class,
GroupModule::class,
ContentModule::class,
CacheModule::class,
CryptoModule::class,
PushersModule::class,
AccountDataModule::class,
ProfileModule::class,
SessionAssistedInjectModule::class
]
modules = [
SessionModule::class,
RoomModule::class,
SyncModule::class,
HomeServerCapabilitiesModule::class,
SignOutModule::class,
GroupModule::class,
UserModule::class,
FilterModule::class,
GroupModule::class,
ContentModule::class,
CacheModule::class,
CryptoModule::class,
PushersModule::class,
AccountDataModule::class,
ProfileModule::class,
SessionAssistedInjectModule::class,
AccountModule::class
]
)
@SessionScope
internal interface SessionComponent {

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.account
import im.vector.matrix.android.api.session.account.model.ChangePasswordParams
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
internal interface AccountAPI {
/**
* Ask the homeserver to change the password with the provided new password.
* @param params parameters to change password.
*/
@Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password")
fun changePassword(@Body params: ChangePasswordParams): Call<Unit>
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.account
import dagger.Binds
import dagger.Module
import dagger.Provides
import im.vector.matrix.android.api.session.account.AccountService
import im.vector.matrix.android.internal.session.SessionScope
import retrofit2.Retrofit
@Module
internal abstract class AccountModule {
@Module
companion object {
@Provides
@JvmStatic
@SessionScope
fun providesAccountAPI(retrofit: Retrofit): AccountAPI {
return retrofit.create(AccountAPI::class.java)
}
}
@Binds
abstract fun bindChangePasswordTask(task: DefaultChangePasswordTask): ChangePasswordTask
@Binds
abstract fun bindAccountService(service: DefaultAccountService): AccountService
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.account
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.account.model.ChangePasswordParams
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject
internal interface ChangePasswordTask : Task<ChangePasswordTask.Params, Unit> {
data class Params(
val password: String,
val newPassword: String
)
}
internal class DefaultChangePasswordTask @Inject constructor(
private val accountAPI: AccountAPI,
private val eventBus: EventBus,
@UserId private val userId: String
) : ChangePasswordTask {
override suspend fun execute(params: ChangePasswordTask.Params) {
val changePasswordParams = ChangePasswordParams.create(userId, params.password, params.newPassword)
try {
executeRequest<Unit>(eventBus) {
apiCall = accountAPI.changePassword(changePasswordParams)
}
} catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError
&& throwable.httpCode == 401
/* Avoid infinite loop */
&& changePasswordParams.auth?.session == null) {
try {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(throwable.errorBody)
} catch (e: Exception) {
null
}?.let {
// Retry with authentication
try {
executeRequest<Unit>(eventBus) {
apiCall = accountAPI.changePassword(
changePasswordParams.copy(auth = changePasswordParams.auth?.copy(session = it.session))
)
}
return
} catch (failure: Throwable) {
throw failure
}
}
}
throw throwable
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.account
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.account.AccountService
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import javax.inject.Inject
internal class DefaultAccountService @Inject constructor(private val changePasswordTask: ChangePasswordTask,
private val taskExecutor: TaskExecutor) : AccountService {
override fun changePassword(password: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
return changePasswordTask
.configureWith(ChangePasswordTask.Params(password, newPassword)) {
this.callback = callback
}
.executeBy(taskExecutor)
}
}

View file

@ -74,7 +74,6 @@ import im.vector.riotx.features.roomprofile.RoomProfileFragment
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.riotx.features.settings.VectorSettingsGeneralFragment
import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
import im.vector.riotx.features.settings.VectorSettingsLabsFragment
import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment
@ -281,11 +280,6 @@ interface FragmentModule {
@FragmentKey(VectorSettingsDevicesFragment::class)
fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsGeneralFragment::class)
fun bindVectorSettingsGeneralFragment(fragment: VectorSettingsGeneralFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PublicRoomsFragment::class)

View file

@ -36,7 +36,6 @@ import com.bumptech.glide.load.engine.cache.DiskCache
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.failure.isInvalidPassword
import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard
@ -59,11 +58,8 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
class VectorSettingsGeneralFragment @Inject constructor(
private val authenticationService: AuthenticationService
) : VectorSettingsBaseFragment() {
class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
override var titleRes = R.string.settings_general_title
override val preferenceXmlRes = R.xml.vector_settings_general
@ -776,63 +772,23 @@ class VectorSettingsGeneralFragment @Inject constructor(
val oldPwd = oldPasswordText.text.toString().trim()
val newPwd = newPasswordText.text.toString().trim()
authenticationService.getLastAuthenticatedSession()?.let {
showPasswordLoadingView(true)
authenticationService.getPasswordWizard().updatePassword(it.sessionId, it.myUserId, oldPwd, newPwd, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
showPasswordLoadingView(false)
dialog.dismiss()
activity.toast(R.string.settings_password_updated)
}
override fun onFailure(failure: Throwable) {
showPasswordLoadingView(false)
if (failure.isInvalidPassword()) {
activity.toast(R.string.settings_fail_to_update_password_invalid_current_password)
} else {
activity.toast(R.string.settings_fail_to_update_password)
}
}
})
}
/* TODO
showPasswordLoadingView(true)
session.updatePassword(oldPwd, newPwd, object : MatrixCallback<Unit> {
private fun onDone(@StringRes textResId: Int) {
session.changePassword(oldPwd, newPwd, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
showPasswordLoadingView(false)
dialog.dismiss()
activity.toast(R.string.settings_password_updated)
}
if (textResId == R.string.settings_fail_to_update_password_invalid_current_password) {
oldPasswordTil.error = getString(textResId)
override fun onFailure(failure: Throwable) {
showPasswordLoadingView(false)
if (failure.isInvalidPassword()) {
activity.toast(R.string.settings_fail_to_update_password_invalid_current_password)
} else {
dialog.dismiss()
activity.toast(textResId, Toast.LENGTH_LONG)
activity.toast(R.string.settings_fail_to_update_password)
}
}
override fun onSuccess(info: Void?) {
onDone(R.string.settings_password_updated)
}
override fun onNetworkError(e: Exception) {
onDone(R.string.settings_fail_to_update_password)
}
override fun onMatrixError(e: MatrixError) {
if (e.error == "Invalid password") {
onDone(R.string.settings_fail_to_update_password_invalid_current_password)
} else {
dialog.dismiss()
onDone(R.string.settings_fail_to_update_password)
}
}
override fun onUnexpectedError(e: Exception) {
onDone(R.string.settings_fail_to_update_password)
}
})
*/
}
}
dialog.show()