mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 20:40:07 +03:00
Identity: validate code received by SMS
This commit is contained in:
parent
e962d1dadf
commit
e411f139c8
17 changed files with 177 additions and 45 deletions
|
@ -158,6 +158,9 @@ dependencies {
|
||||||
// Bus
|
// Bus
|
||||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
|
|
||||||
|
// Phone number https://github.com/google/libphonenumber
|
||||||
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.10.23'
|
||||||
|
|
||||||
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0'
|
||||||
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
releaseImplementation 'com.airbnb.okreplay:noop:1.5.0'
|
||||||
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0'
|
||||||
|
|
|
@ -23,9 +23,9 @@ import im.vector.matrix.android.api.util.Cancelable
|
||||||
* Provides access to the identity server configuration and services identity server can provide
|
* Provides access to the identity server configuration and services identity server can provide
|
||||||
*/
|
*/
|
||||||
interface IdentityService {
|
interface IdentityService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default identity server of the homeserver (using Wellknown request)
|
* Return the default identity server of the homeserver (using Wellknown request).
|
||||||
|
* It may be different from the current configured identity server
|
||||||
*/
|
*/
|
||||||
fun getDefaultIdentityServer(callback: MatrixCallback<String?>): Cancelable
|
fun getDefaultIdentityServer(callback: MatrixCallback<String?>): Cancelable
|
||||||
|
|
||||||
|
@ -51,9 +51,11 @@ interface IdentityService {
|
||||||
fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param code the code sent to the user phone number
|
* Submit the code that the identity server has sent to the user (in email or SMS)
|
||||||
|
* Once successful, you will have to call [finalizeBindThreePid]
|
||||||
|
* @param code the code sent to the user
|
||||||
*/
|
*/
|
||||||
fun submitValidationToken(pid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable
|
fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request will actually be done on the homeserver
|
* The request will actually be done on the homeserver
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.identity
|
package im.vector.matrix.android.api.session.identity
|
||||||
|
|
||||||
|
import com.google.i18n.phonenumbers.NumberParseException
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||||
import im.vector.matrix.android.internal.session.profile.ThirdPartyIdentifier
|
import im.vector.matrix.android.internal.session.profile.ThirdPartyIdentifier
|
||||||
|
|
||||||
sealed class ThreePid(open val value: String) {
|
sealed class ThreePid(open val value: String) {
|
||||||
data class Email(val email: String) : ThreePid(email)
|
data class Email(val email: String) : ThreePid(email)
|
||||||
data class Msisdn(val msisdn: String, val countryCode: String? = null) : ThreePid(msisdn)
|
data class Msisdn(val msisdn: String) : ThreePid(msisdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ThreePid.toMedium(): String {
|
internal fun ThreePid.toMedium(): String {
|
||||||
|
@ -29,3 +31,10 @@ internal fun ThreePid.toMedium(): String {
|
||||||
is ThreePid.Msisdn -> ThirdPartyIdentifier.MEDIUM_MSISDN
|
is ThreePid.Msisdn -> ThirdPartyIdentifier.MEDIUM_MSISDN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(NumberParseException::class)
|
||||||
|
internal fun ThreePid.Msisdn.getCountryCode(): String {
|
||||||
|
return with(PhoneNumberUtil.getInstance()) {
|
||||||
|
getRegionCodeForCountryCode(parse("+$msisdn", null).countryCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ internal class DefaultBulkLookupTask @Inject constructor(
|
||||||
executeRequest(null) {
|
executeRequest(null) {
|
||||||
apiCall = identityAPI.lookup(IdentityLookUpParams(
|
apiCall = identityAPI.lookup(IdentityLookUpParams(
|
||||||
hashedAddresses,
|
hashedAddresses,
|
||||||
"sha256",
|
IdentityHashDetailResponse.ALGORITHM_SHA256,
|
||||||
hashDetailResponse.pepper
|
hashDetailResponse.pepper
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ internal class DefaultBulkLookupTask @Inject constructor(
|
||||||
// Retrieve the new hash details
|
// Retrieve the new hash details
|
||||||
val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI)
|
val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI)
|
||||||
|
|
||||||
if (hashDetailResponse.algorithms.contains("sha256").not()) {
|
if (hashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) {
|
||||||
// TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it
|
// TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it
|
||||||
// Also, what we have in cache is maybe outdated, the identity server maybe now support sha256
|
// Also, what we have in cache is maybe outdated, the identity server maybe now support sha256
|
||||||
throw IdentityServiceError.BulkLookupSha256NotSupported
|
throw IdentityServiceError.BulkLookupSha256NotSupported
|
||||||
|
|
|
@ -71,6 +71,7 @@ internal class DefaultIdentityService @Inject constructor(
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
private val bindThreePidsTask: BindThreePidsTask,
|
private val bindThreePidsTask: BindThreePidsTask,
|
||||||
|
private val submitTokenForBindingTask: IdentitySubmitTokenForBindingTask,
|
||||||
private val unbindThreePidsTask: UnbindThreePidsTask,
|
private val unbindThreePidsTask: UnbindThreePidsTask,
|
||||||
private val identityApiProvider: IdentityApiProvider,
|
private val identityApiProvider: IdentityApiProvider,
|
||||||
private val accountDataDataSource: AccountDataDataSource
|
private val accountDataDataSource: AccountDataDataSource
|
||||||
|
@ -132,8 +133,10 @@ internal class DefaultIdentityService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun submitValidationToken(pid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable {
|
override fun submitValidationToken(threePid: ThreePid, code: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
TODO("Not yet implemented")
|
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
|
submitTokenForBindingTask.execute(IdentitySubmitTokenForBindingTask.Params(threePid, code))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable {
|
override fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.identity
|
package im.vector.matrix.android.internal.session.identity
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.SuccessResult
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityAccountResponse
|
import im.vector.matrix.android.internal.session.identity.model.IdentityAccountResponse
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityHashDetailResponse
|
import im.vector.matrix.android.internal.session.identity.model.IdentityHashDetailResponse
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityLookUpParams
|
import im.vector.matrix.android.internal.session.identity.model.IdentityLookUpParams
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityLookUpResponse
|
import im.vector.matrix.android.internal.session.identity.model.IdentityLookUpResponse
|
||||||
|
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestOwnershipParams
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenForEmailBody
|
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenForEmailBody
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenForMsisdnBody
|
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenForMsisdnBody
|
||||||
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenResponse
|
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestTokenResponse
|
||||||
|
@ -28,6 +30,7 @@ import retrofit2.Call
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ref: https://matrix.org/docs/spec/identity_service/latest
|
* Ref: https://matrix.org/docs/spec/identity_service/latest
|
||||||
|
@ -83,4 +86,13 @@ internal interface IdentityAPI {
|
||||||
*/
|
*/
|
||||||
@POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "validate/msisdn/requestToken")
|
@POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "validate/msisdn/requestToken")
|
||||||
fun requestTokenToBindMsisdn(@Body body: IdentityRequestTokenForMsisdnBody): Call<IdentityRequestTokenResponse>
|
fun requestTokenToBindMsisdn(@Body body: IdentityRequestTokenForMsisdnBody): Call<IdentityRequestTokenResponse>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate ownership of an email address, or a phone number.
|
||||||
|
* Ref:
|
||||||
|
* - https://matrix.org/docs/spec/identity_service/latest#post-matrix-identity-v2-validate-msisdn-submittoken
|
||||||
|
* - https://matrix.org/docs/spec/identity_service/latest#post-matrix-identity-v2-validate-email-submittoken
|
||||||
|
*/
|
||||||
|
@POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "validate/{medium}/submitToken")
|
||||||
|
fun submitToken(@Path("medium") medium: String, @Body body: IdentityRequestOwnershipParams): Call<SuccessResult>
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,6 @@ internal abstract class IdentityModule {
|
||||||
.deleteRealmIfMigrationNeeded()
|
.deleteRealmIfMigrationNeeded()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
|
@ -99,6 +98,9 @@ internal abstract class IdentityModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindIdentityRequestTokenForBindingTask(task: DefaultIdentityRequestTokenForBindingTask): IdentityRequestTokenForBindingTask
|
abstract fun bindIdentityRequestTokenForBindingTask(task: DefaultIdentityRequestTokenForBindingTask): IdentityRequestTokenForBindingTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindIdentitySubmitTokenForBindingTask(task: DefaultIdentitySubmitTokenForBindingTask): IdentitySubmitTokenForBindingTask
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindBulkLookupTask(task: DefaultBulkLookupTask): BulkLookupTask
|
abstract fun bindBulkLookupTask(task: DefaultBulkLookupTask): BulkLookupTask
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.identity
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.identity.IdentityServiceError
|
import im.vector.matrix.android.api.session.identity.IdentityServiceError
|
||||||
import im.vector.matrix.android.api.session.identity.ThreePid
|
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||||
|
import im.vector.matrix.android.api.session.identity.getCountryCode
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.identity.db.RealmIdentityServiceStore
|
import im.vector.matrix.android.internal.session.identity.db.RealmIdentityServiceStore
|
||||||
|
@ -52,12 +53,14 @@ internal class DefaultIdentityRequestTokenForBindingTask @Inject constructor(
|
||||||
sendAttempt = 1,
|
sendAttempt = 1,
|
||||||
email = params.threePid.email
|
email = params.threePid.email
|
||||||
))
|
))
|
||||||
is ThreePid.Msisdn -> identityAPI.requestTokenToBindMsisdn(IdentityRequestTokenForMsisdnBody(
|
is ThreePid.Msisdn -> {
|
||||||
clientSecret = clientSecret,
|
identityAPI.requestTokenToBindMsisdn(IdentityRequestTokenForMsisdnBody(
|
||||||
sendAttempt = 1,
|
clientSecret = clientSecret,
|
||||||
phoneNumber = params.threePid.msisdn,
|
sendAttempt = 1,
|
||||||
countryCode = params.threePid.countryCode
|
phoneNumber = params.threePid.msisdn,
|
||||||
))
|
countryCode = params.threePid.getCountryCode()
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.identity
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.identity.IdentityServiceError
|
||||||
|
import im.vector.matrix.android.api.session.identity.ThreePid
|
||||||
|
import im.vector.matrix.android.api.session.identity.toMedium
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.SuccessResult
|
||||||
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
|
import im.vector.matrix.android.internal.session.identity.db.RealmIdentityServiceStore
|
||||||
|
import im.vector.matrix.android.internal.session.identity.model.IdentityRequestOwnershipParams
|
||||||
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface IdentitySubmitTokenForBindingTask : Task<IdentitySubmitTokenForBindingTask.Params, Unit> {
|
||||||
|
data class Params(
|
||||||
|
val threePid: ThreePid,
|
||||||
|
val token: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultIdentitySubmitTokenForBindingTask @Inject constructor(
|
||||||
|
private val identityApiProvider: IdentityApiProvider,
|
||||||
|
private val identityServiceStore: RealmIdentityServiceStore,
|
||||||
|
@UserId private val userId: String
|
||||||
|
) : IdentitySubmitTokenForBindingTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: IdentitySubmitTokenForBindingTask.Params) {
|
||||||
|
val identityAPI = getIdentityApiAndEnsureTerms(identityApiProvider, userId)
|
||||||
|
val pendingThreePid = identityServiceStore.getPendingBinding(params.threePid) ?: throw IdentityServiceError.NoCurrentBindingError
|
||||||
|
|
||||||
|
val tokenResponse = executeRequest<SuccessResult>(null) {
|
||||||
|
apiCall = identityAPI.submitToken(
|
||||||
|
params.threePid.toMedium(),
|
||||||
|
IdentityRequestOwnershipParams(
|
||||||
|
clientSecret = pendingThreePid.clientSecret,
|
||||||
|
sid = pendingThreePid.sid,
|
||||||
|
token = params.token
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenResponse.isSuccess()) {
|
||||||
|
throw IdentityServiceError.BindingError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,4 +37,9 @@ internal data class IdentityHashDetailResponse(
|
||||||
*/
|
*/
|
||||||
@Json(name = "algorithms")
|
@Json(name = "algorithms")
|
||||||
val algorithms: List<String>
|
val algorithms: List<String>
|
||||||
)
|
) {
|
||||||
|
companion object{
|
||||||
|
const val ALGORITHM_SHA256 = "sha256"
|
||||||
|
const val ALGORITHM_NONE = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,12 +20,21 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class IdentityRequestOwnershipParams(
|
internal data class IdentityRequestOwnershipParams(
|
||||||
|
/**
|
||||||
|
* Required. The client secret that was supplied to the requestToken call.
|
||||||
|
*/
|
||||||
@Json(name = "client_secret")
|
@Json(name = "client_secret")
|
||||||
var clientSecret: String? = null,
|
val clientSecret: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The session ID, generated by the requestToken call.
|
||||||
|
*/
|
||||||
@Json(name = "sid")
|
@Json(name = "sid")
|
||||||
var sid: String? = null,
|
val sid: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The token generated by the requestToken call and sent to the user.
|
||||||
|
*/
|
||||||
@Json(name = "token")
|
@Json(name = "token")
|
||||||
var token: String? = null
|
val token: String
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,13 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
// Just to consider common parameters
|
// Just to consider common parameters
|
||||||
private interface IdentityRequestTokenBody {
|
private interface IdentityRequestTokenBody {
|
||||||
|
/**
|
||||||
|
* Required. A unique string generated by the client, and used to identify the validation attempt.
|
||||||
|
* It must be a string consisting of the characters [0-9a-zA-Z.=_-].
|
||||||
|
* Its length must not exceed 255 characters and it must not be empty.
|
||||||
|
*/
|
||||||
val clientSecret: String
|
val clientSecret: String
|
||||||
|
|
||||||
val sendAttempt: Int
|
val sendAttempt: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +36,19 @@ internal data class IdentityRequestTokenForEmailBody(
|
||||||
@Json(name = "client_secret")
|
@Json(name = "client_secret")
|
||||||
override val clientSecret: String,
|
override val clientSecret: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The server will only send an email if the send_attempt is a number greater than the most
|
||||||
|
* recent one which it has seen, scoped to that email + client_secret pair. This is to avoid repeatedly
|
||||||
|
* sending the same email in the case of request retries between the POSTing user and the identity server.
|
||||||
|
* The client should increment this value if they desire a new email (e.g. a reminder) to be sent.
|
||||||
|
* If they do not, the server should respond with success but not resend the email.
|
||||||
|
*/
|
||||||
@Json(name = "send_attempt")
|
@Json(name = "send_attempt")
|
||||||
override val sendAttempt: Int,
|
override val sendAttempt: Int,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The email address to validate.
|
||||||
|
*/
|
||||||
@Json(name = "email")
|
@Json(name = "email")
|
||||||
val email: String
|
val email: String
|
||||||
) : IdentityRequestTokenBody
|
) : IdentityRequestTokenBody
|
||||||
|
@ -42,12 +58,25 @@ internal data class IdentityRequestTokenForMsisdnBody(
|
||||||
@Json(name = "client_secret")
|
@Json(name = "client_secret")
|
||||||
override val clientSecret: String,
|
override val clientSecret: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The server will only send an SMS if the send_attempt is a number greater than the most recent one
|
||||||
|
* which it has seen, scoped to that country + phone_number + client_secret triple. This is to avoid repeatedly
|
||||||
|
* sending the same SMS in the case of request retries between the POSTing user and the identity server.
|
||||||
|
* The client should increment this value if they desire a new SMS (e.g. a reminder) to be sent.
|
||||||
|
*/
|
||||||
@Json(name = "send_attempt")
|
@Json(name = "send_attempt")
|
||||||
override val sendAttempt: Int,
|
override val sendAttempt: Int,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The phone number to validate.
|
||||||
|
*/
|
||||||
@Json(name = "phone_number")
|
@Json(name = "phone_number")
|
||||||
val phoneNumber: String,
|
val phoneNumber: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in phone_number
|
||||||
|
* should be parsed as if it were dialled from.
|
||||||
|
*/
|
||||||
@Json(name = "country")
|
@Json(name = "country")
|
||||||
val countryCode: String?
|
val countryCode: String
|
||||||
) : IdentityRequestTokenBody
|
) : IdentityRequestTokenBody
|
||||||
|
|
|
@ -21,9 +21,17 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class IdentityRequestTokenResponse(
|
internal data class IdentityRequestTokenResponse(
|
||||||
|
/**
|
||||||
|
* Required. The session ID. Session IDs are opaque strings generated by the identity server.
|
||||||
|
* They must consist entirely of the characters [0-9a-zA-Z.=_-].
|
||||||
|
* Their length must not exceed 255 characters and they must not be empty.
|
||||||
|
*/
|
||||||
@Json(name = "sid")
|
@Json(name = "sid")
|
||||||
val sid: String,
|
val sid: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not documented
|
||||||
|
*/
|
||||||
@Json(name = "success")
|
@Json(name = "success")
|
||||||
val success: Boolean
|
val success: Boolean
|
||||||
)
|
)
|
||||||
|
|
|
@ -132,7 +132,7 @@ class DiscoverySettingsController @Inject constructor(
|
||||||
}
|
}
|
||||||
SharedState.BINDING_IN_PROGRESS -> {
|
SharedState.BINDING_IN_PROGRESS -> {
|
||||||
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
|
buttonType(SettingsTextButtonItem.ButtonType.NORMAL)
|
||||||
buttonTitle("")
|
buttonTitle(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ class DiscoverySettingsController @Inject constructor(
|
||||||
interactionListener(object : SettingsItemText.Listener {
|
interactionListener(object : SettingsItemText.Listener {
|
||||||
override fun onValidate(code: String) {
|
override fun onValidate(code: String) {
|
||||||
if (piState.threePid is ThreePid.Msisdn) {
|
if (piState.threePid is ThreePid.Msisdn) {
|
||||||
listener?.checkMsisdnVerification(piState.threePid, code)
|
listener?.sendMsisdnVerificationCode(piState.threePid, code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -299,7 +299,7 @@ class DiscoverySettingsController @Inject constructor(
|
||||||
fun onTapRevoke(threePid: ThreePid)
|
fun onTapRevoke(threePid: ThreePid)
|
||||||
fun onTapShare(threePid: ThreePid)
|
fun onTapShare(threePid: ThreePid)
|
||||||
fun checkEmailVerification(threePid: ThreePid.Email)
|
fun checkEmailVerification(threePid: ThreePid.Email)
|
||||||
fun checkMsisdnVerification(threePid: ThreePid.Msisdn, code: String)
|
fun sendMsisdnVerificationCode(threePid: ThreePid.Msisdn, code: String)
|
||||||
fun onTapChangeIdentityServer()
|
fun onTapChangeIdentityServer()
|
||||||
fun onTapDisconnectIdentityServer()
|
fun onTapDisconnectIdentityServer()
|
||||||
fun onTapRetryToRetrieveBindings()
|
fun onTapRetryToRetrieveBindings()
|
||||||
|
|
|
@ -67,7 +67,7 @@ class DiscoverySettingsFragment @Inject constructor(
|
||||||
viewModel.observeViewEvents {
|
viewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is DiscoverySettingsViewEvents.Failure -> {
|
is DiscoverySettingsViewEvents.Failure -> {
|
||||||
// TODO Snackbar.make(view, throwable.toString(), Snackbar.LENGTH_LONG).show()
|
displayErrorDialog(it.throwable)
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ class DiscoverySettingsFragment @Inject constructor(
|
||||||
viewModel.handle(DiscoverySettingsAction.FinalizeBind3pid(threePid))
|
viewModel.handle(DiscoverySettingsAction.FinalizeBind3pid(threePid))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun checkMsisdnVerification(threePid: ThreePid.Msisdn, code: String) {
|
override fun sendMsisdnVerificationCode(threePid: ThreePid.Msisdn, code: String) {
|
||||||
viewModel.handle(DiscoverySettingsAction.SubmitMsisdnToken(threePid, code))
|
viewModel.handle(DiscoverySettingsAction.SubmitMsisdnToken(threePid, code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
@ -166,18 +165,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
||||||
if (state.identityServer() == null) return@withState
|
if (state.identityServer() == null) return@withState
|
||||||
changeThreePidState(action.threePid, Loading())
|
changeThreePidState(action.threePid, Loading())
|
||||||
|
|
||||||
val threePid = if (action.threePid is ThreePid.Msisdn && action.threePid.countryCode == null) {
|
identityService.startBindThreePid(action.threePid, object : MatrixCallback<Unit> {
|
||||||
// Ensure we have a country code
|
|
||||||
|
|
||||||
val phoneNumber = PhoneNumberUtil.getInstance()
|
|
||||||
.parse("+${action.threePid.msisdn}", null)
|
|
||||||
action.threePid.copy(countryCode = PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(phoneNumber.countryCode)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
action.threePid
|
|
||||||
}
|
|
||||||
|
|
||||||
identityService.startBindThreePid(threePid, object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
changeThreePidState(action.threePid, Success(SharedState.BINDING_IN_PROGRESS))
|
changeThreePidState(action.threePid, Success(SharedState.BINDING_IN_PROGRESS))
|
||||||
}
|
}
|
||||||
|
@ -286,8 +274,8 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
||||||
override fun onSuccess(data: Map<ThreePid, SharedState>) {
|
override fun onSuccess(data: Map<ThreePid, SharedState>) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
emailList = Success(toPidInfoList(data.filter { it.key is ThreePid.Email })),
|
emailList = Success(data.filter { it.key is ThreePid.Email }.toPidInfoList()),
|
||||||
phoneNumbersList = Success(toPidInfoList(data.filter { it.key is ThreePid.Msisdn }))
|
phoneNumbersList = Success(data.filter { it.key is ThreePid.Msisdn }.toPidInfoList())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,8 +300,8 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toPidInfoList(threePidStatuses: Map<ThreePid, SharedState>): List<PidInfo> {
|
private fun Map<ThreePid, SharedState>.toPidInfoList(): List<PidInfo> {
|
||||||
return threePidStatuses.map { threePidStatus ->
|
return map { threePidStatus ->
|
||||||
PidInfo(
|
PidInfo(
|
||||||
threePid = threePidStatus.key,
|
threePid = threePidStatus.key,
|
||||||
isShared = Success(threePidStatus.value)
|
isShared = Success(threePidStatus.value)
|
||||||
|
@ -328,7 +316,6 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
|
||||||
action.code,
|
action.code,
|
||||||
object : MatrixCallback<Unit> {
|
object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// TODO This should be done in the task
|
|
||||||
finalizeBind3pid(DiscoverySettingsAction.FinalizeBind3pid(action.threePid))
|
finalizeBind3pid(DiscoverySettingsAction.FinalizeBind3pid(action.threePid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
android:background="?attr/colorBackgroundFloating"
|
android:background="?attr/colorBackgroundFloating"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="@dimen/layout_horizontal_margin"
|
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||||
android:paddingTop="@dimen/layout_vertical_margin"
|
|
||||||
android:paddingEnd="@dimen/layout_horizontal_margin"
|
android:paddingEnd="@dimen/layout_horizontal_margin"
|
||||||
android:paddingBottom="@dimen/layout_vertical_margin">
|
android:paddingBottom="@dimen/layout_vertical_margin">
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue