mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Update Self Verification BottomSheet for quads
This commit is contained in:
parent
cb73955946
commit
3dc89c8d87
21 changed files with 112 additions and 70 deletions
|
@ -53,6 +53,8 @@ interface CrossSigningService {
|
|||
fun trustUser(otherUserId: String,
|
||||
callback: MatrixCallback<Unit>)
|
||||
|
||||
fun markMyMasterKeyAsTrusted()
|
||||
|
||||
/**
|
||||
* Sign one of your devices and upload the signature
|
||||
*/
|
||||
|
|
|
@ -374,7 +374,9 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
?.fromBase64NoPadding()
|
||||
|
||||
var isMaterKeyTrusted = false
|
||||
if (masterPrivateKey != null) {
|
||||
if (myMasterKey.trustLevel?.locallyVerified == true) {
|
||||
isMaterKeyTrusted = true
|
||||
} else if (masterPrivateKey != null) {
|
||||
// Check if private match public
|
||||
var olmPkSigning: OlmPkSigning? = null
|
||||
try {
|
||||
|
@ -507,6 +509,11 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun markMyMasterKeyAsTrusted() {
|
||||
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
|
||||
checkSelfTrust()
|
||||
}
|
||||
|
||||
override fun signDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||
// This device should be yours
|
||||
val device = cryptoStore.getUserDevice(userId, deviceId)
|
||||
|
|
|
@ -413,6 +413,8 @@ internal interface IMXCryptoStore {
|
|||
fun getLiveCrossSigningInfo(userId: String) : LiveData<Optional<MXCrossSigningInfo>>
|
||||
fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
|
||||
|
||||
fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean)
|
||||
|
||||
fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
|
||||
fun getCrossSigningPrivateKeys() : PrivateKeysInfo?
|
||||
|
||||
|
|
|
@ -1094,6 +1094,24 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean) {
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()?.userId?.let { myUserId ->
|
||||
CrossSigningInfoEntity.get(realm, myUserId)?.getMasterKey()?.let { xInfoEntity ->
|
||||
val level = xInfoEntity.trustLevelEntity
|
||||
if (level == null) {
|
||||
val newLevel = realm.createObject(TrustLevelEntity::class.java)
|
||||
newLevel.locallyVerified = trusted
|
||||
xInfoEntity.trustLevelEntity = newLevel
|
||||
} else {
|
||||
level.locallyVerified = trusted
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? {
|
||||
var existing = CrossSigningInfoEntity.get(realm, userId)
|
||||
if (info == null) {
|
||||
|
|
|
@ -222,14 +222,19 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||
|
||||
private fun trust(canTrustOtherUserMasterKey: Boolean, toVerifyDeviceIds: List<String>) {
|
||||
// If not me sign his MSK and upload the signature
|
||||
if (otherUserId != userId && canTrustOtherUserMasterKey) {
|
||||
// we should trust this master key
|
||||
// And check verification MSK -> SSK?
|
||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
||||
}
|
||||
})
|
||||
if (canTrustOtherUserMasterKey) {
|
||||
if (otherUserId != userId ) {
|
||||
// we should trust this master key
|
||||
// And check verification MSK -> SSK?
|
||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
//Mark my keys as trusted locally
|
||||
crossSigningService.markMyMasterKeyAsTrusted()
|
||||
}
|
||||
}
|
||||
|
||||
if (otherUserId == userId) {
|
||||
|
|
|
@ -28,4 +28,5 @@ sealed class VerificationAction : VectorViewModelAction {
|
|||
data class SASMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction()
|
||||
data class SASDoNotMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction()
|
||||
object GotItConclusion : VerificationAction()
|
||||
object SkipVerification : VerificationAction()
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import butterknife.BindView
|
|||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
|
@ -54,8 +55,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
val otherUserId: String,
|
||||
val verificationId: String? = null,
|
||||
val roomId: String? = null,
|
||||
// Special mode where UX should show loading wheel until other user sends a request/tx
|
||||
val waitForIncomingRequest: Boolean = false
|
||||
// Special mode where UX should show loading wheel until other session sends a request/tx
|
||||
val selfVerificationMode: Boolean = false
|
||||
) : Parcelable
|
||||
|
||||
@Inject
|
||||
|
@ -183,7 +184,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
// If it's an outgoing
|
||||
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.waitForOtherUserMode) {
|
||||
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.selfVerificationMode) {
|
||||
Timber.v("## SAS show bottom sheet for outgoing request")
|
||||
if (state.pendingRequest.invoke()?.isReady == true) {
|
||||
Timber.v("## SAS show bottom sheet for outgoing and ready request")
|
||||
|
@ -230,14 +231,25 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null, waitForIncomingRequest: Boolean = false): VerificationBottomSheet {
|
||||
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet {
|
||||
return VerificationBottomSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||
otherUserId = otherUserId,
|
||||
roomId = roomId,
|
||||
verificationId = transactionId,
|
||||
waitForIncomingRequest = waitForIncomingRequest
|
||||
selfVerificationMode = false
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun forSelfVerification(session: Session) : VerificationBottomSheet{
|
||||
return VerificationBottomSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationArgs(
|
||||
otherUserId = session.myUserId,
|
||||
selfVerificationMode = true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ data class VerificationBottomSheetViewState(
|
|||
val qrTransactionState: VerificationTxState? = null,
|
||||
val transactionId: String? = null,
|
||||
// true when we display the loading and we wait for the other (incoming request)
|
||||
val waitForOtherUserMode: Boolean = false,
|
||||
val selfVerificationMode: Boolean = false,
|
||||
val isMe: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
|
@ -67,10 +67,10 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
|
||||
val userItem = session.getUser(args.otherUserId)
|
||||
|
||||
val isWaitingForOtherMode = args.waitForIncomingRequest
|
||||
val selfVerificationMode = args.selfVerificationMode
|
||||
|
||||
var autoReady = false
|
||||
val pr = if (isWaitingForOtherMode) {
|
||||
val pr = if (selfVerificationMode) {
|
||||
// See if active tx for this user and take it
|
||||
|
||||
session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId)
|
||||
|
@ -100,7 +100,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
qrTransactionState = qrTx?.state,
|
||||
transactionId = pr?.transactionId ?: args.verificationId,
|
||||
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
|
||||
waitForOtherUserMode = isWaitingForOtherMode,
|
||||
selfVerificationMode = selfVerificationMode,
|
||||
roomId = args.roomId,
|
||||
isMe = args.otherUserId == session.myUserId
|
||||
)
|
||||
|
@ -250,6 +250,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
is VerificationAction.GotItConclusion -> {
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
|
||||
}
|
||||
is VerificationAction.SkipVerification -> {
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -258,7 +261,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
}
|
||||
|
||||
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
|
||||
if (state.waitForOtherUserMode && state.transactionId == null) {
|
||||
if (state.selfVerificationMode && state.transactionId == null) {
|
||||
// is this an incoming with that user
|
||||
if (tx.isIncoming && tx.otherUserId == state.otherUserMxItem?.id) {
|
||||
// Also auto accept incoming if needed!
|
||||
|
@ -308,7 +311,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
|
||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
|
||||
|
||||
if (state.waitForOtherUserMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
|
||||
if (state.selfVerificationMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
|
||||
// is this an incoming with that user
|
||||
if (pr.isIncoming && pr.otherUserId == state.otherUserMxItem?.id) {
|
||||
if (!pr.isReady) {
|
||||
|
|
|
@ -50,7 +50,7 @@ class VerificationRequestController @Inject constructor(
|
|||
val state = viewState ?: return
|
||||
val matrixItem = viewState?.otherUserMxItem ?: return
|
||||
|
||||
if (state.waitForOtherUserMode) {
|
||||
if (state.selfVerificationMode) {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_open_other_to_verify))
|
||||
|
@ -62,8 +62,28 @@ class VerificationRequestController @Inject constructor(
|
|||
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
|
||||
title(stringProvider.getString(R.string.verification_request_waiting, matrixItem.getBestName()))
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("passphrase")
|
||||
title(stringProvider.getString(R.string.verification_cannot_access_other_session))
|
||||
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
subTitle(stringProvider.getString(R.string.verification_use_passphrase))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onClickRecoverFromPassphrase() }
|
||||
}
|
||||
bottomSheetVerificationActionItem {
|
||||
id("skip")
|
||||
title(stringProvider.getString(R.string.skip))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
// subTitle(stringProvider.getString(R.string.verification_use_passphrase))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
listener { listener?.onClickDismiss() }
|
||||
}
|
||||
|
||||
} else {
|
||||
val styledText = matrixItem.let {
|
||||
stringProvider.getString(R.string.verification_request_notice, it.id)
|
||||
|
@ -112,5 +132,7 @@ class VerificationRequestController @Inject constructor(
|
|||
|
||||
interface Listener {
|
||||
fun onClickOnVerificationStart()
|
||||
fun onClickRecoverFromPassphrase()
|
||||
fun onClickDismiss()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,4 +61,11 @@ class VerificationRequestFragment @Inject constructor(
|
|||
viewModel.handle(VerificationAction.RequestVerificationByDM(otherUserId, state.roomId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClickRecoverFromPassphrase() {
|
||||
}
|
||||
|
||||
override fun onClickDismiss() {
|
||||
viewModel.handle(VerificationAction.SkipVerification)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,41 +145,12 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
val crossSigningEnabledOnAccount = myCrossSigningKeys != null
|
||||
|
||||
if (crossSigningEnabledOnAccount && myCrossSigningKeys?.isTrusted() == false) {
|
||||
|
||||
// We need to ask
|
||||
sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true
|
||||
PopupAlertManager.postVectorAlert(
|
||||
PopupAlertManager.VectorAlert(
|
||||
uid = "completeSecurity",
|
||||
title = getString(R.string.crosssigning_verify_this_session),
|
||||
description = getString(R.string.crosssigning_other_user_not_trust),
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
|
||||
contentAction = Runnable {
|
||||
Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
it.navigator.waitSessionVerification(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
// tx.cancel()
|
||||
}
|
||||
addButton(
|
||||
getString(R.string.later),
|
||||
Runnable {
|
||||
}
|
||||
)
|
||||
addButton(
|
||||
getString(R.string.verification_profile_verify),
|
||||
Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
|
||||
it.navigator.waitSessionVerification(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
navigator.waitSessionVerification(this)
|
||||
} else {
|
||||
// TODO upgrade security -> bootstrap cross signing
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,12 +95,8 @@ class DefaultNavigator @Inject constructor(
|
|||
override fun waitSessionVerification(context: Context) {
|
||||
val session = sessionHolder.getSafeActiveSession() ?: return
|
||||
if (context is VectorBaseActivity) {
|
||||
VerificationBottomSheet.withArgs(
|
||||
roomId = null,
|
||||
otherUserId = session.myUserId,
|
||||
waitForIncomingRequest = true
|
||||
|
||||
).show(context.supportFragmentManager, VerificationBottomSheet.WAITING_SELF_VERIF_TAG)
|
||||
VerificationBottomSheet.forSelfVerification(session)
|
||||
.show(context.supportFragmentManager, VerificationBottomSheet.WAITING_SELF_VERIF_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
android:id="@+id/bottomSheetFragmentContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/verificationRequestAvatar" />
|
||||
|
|
|
@ -2140,7 +2140,6 @@ Abisua: Fitxategi hau ezabatu daiteke aplikazioa desinstalatzen bada.</string>
|
|||
<item quantity="other">%d saio aktibo</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Egiaztatu saio hau</string>
|
||||
<string name="crosssigning_other_user_not_trust">Beste erabiltzaile batzuk ez fidagarritzat jo lezakete</string>
|
||||
<string name="complete_security">Bete segurtasuna</string>
|
||||
|
||||
|
|
|
@ -2148,7 +2148,6 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq
|
|||
<item quantity="other">%d sessions actives</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Vérifier cette session</string>
|
||||
<string name="crosssigning_other_user_not_trust">Les autres utilisateurs ne lui font peut-être pas confiance</string>
|
||||
<string name="complete_security">Compléter la sécurité</string>
|
||||
|
||||
|
|
|
@ -2143,7 +2143,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
|
|||
<item quantity="other">%d munkamenet használatban</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Munkamenet ellenőrzése</string>
|
||||
<string name="crosssigning_other_user_not_trust">Más felhasználók lehet, hogy nem bíznak benne</string>
|
||||
<string name="complete_security">Biztonság beállítása</string>
|
||||
|
||||
|
|
|
@ -2193,7 +2193,6 @@
|
|||
<item quantity="other">%d sessioni attive</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Verifica questa sessione</string>
|
||||
<string name="crosssigning_other_user_not_trust">Gli altri utenti potrebbero non fidarsi</string>
|
||||
<string name="complete_security">Completa la sicurezza</string>
|
||||
|
||||
|
|
|
@ -2062,7 +2062,6 @@ Që të garantoni se s’ju shpëton gjë, thjesht mbajeni të aktivizuar mekani
|
|||
<item quantity="other">%d sesione aktive</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Verifikoni këtë sesion</string>
|
||||
<string name="crosssigning_other_user_not_trust">Përdorues të tjerë mund të mos e besojnë</string>
|
||||
<string name="complete_security">Siguri e Plotë</string>
|
||||
|
||||
|
|
|
@ -2093,7 +2093,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
|
|||
<item quantity="other">%d 活躍的工作階段</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">驗證此工作階段</string>
|
||||
<string name="crosssigning_other_user_not_trust">其他使用者可能不會信任它</string>
|
||||
<string name="complete_security">全面的安全性</string>
|
||||
|
||||
|
|
|
@ -2121,11 +2121,10 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
|
|||
<item quantity="other">%d active sessions</item>
|
||||
</plurals>
|
||||
|
||||
<string name="crosssigning_verify_this_session">Verify this session</string>
|
||||
<string name="crosssigning_other_user_not_trust">Other users may not trust it</string>
|
||||
<string name="complete_security">Complete Security</string>
|
||||
|
||||
<string name="verification_open_other_to_verify">Open an existing session & use it to verify this one, granting it access to encrypted messages. If you can’t access one, use your recovery key or passphrase.</string>
|
||||
<string name="verification_open_other_to_verify">Open an existing session & use it to verify this one, granting it access to encrypted messages.</string>
|
||||
|
||||
|
||||
<string name="verification_profile_verify">Verify</string>
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
</plurals>
|
||||
<string name="poll_item_selected_aria">Selected Option</string>
|
||||
<string name="command_description_poll">Creates a simple poll</string>
|
||||
|
||||
<string name="verification_cannot_access_other_session">Can‘t access an existing session?</string>
|
||||
<string name="verification_use_passphrase">Use your recovery key or passphrase</string>
|
||||
<!-- END Strings added by Valere -->
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue