From aa0937e6aafa00ac0e326ed16cb22a1119e72ab5 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 15 Oct 2019 14:47:42 -0300 Subject: [PATCH] [android] Show dialog when (pending) contact already exists If two different people sent the same link, show warning dialog to the user to prevent a social attack trying to discover contact relationships. --- .../add/remote/AddContactViewModel.java | 16 +++ .../contact/add/remote/NicknameFragment.java | 113 +++++++++++++++--- .../keyagreement/ContactExchangeActivity.java | 3 +- .../src/main/res/values-zh-rCN/strings.xml | 2 +- briar-android/src/main/res/values/strings.xml | 1 + 5 files changed, 117 insertions(+), 18 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java index 718cdc83d..901f5773d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java @@ -9,6 +9,7 @@ import android.support.annotation.Nullable; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -25,6 +26,7 @@ import javax.inject.Inject; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX; +import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.util.LogUtils.logException; @NotNullByDefault @@ -118,4 +120,18 @@ public class AddContactViewModel extends AndroidViewModel { return addContactResult; } + public void updatePendingContact(String name, PendingContact p) { + dbExecutor.execute(() -> { + if (contactManager.getPendingContactState(p.getId()) == FAILED) { + try { + contactManager.removePendingContact(p.getId()); + } catch (DbException e) { + logException(LOG, WARNING, e); + addContactResult.postValue(new LiveResult<>(e)); + } + addContact(name); + } + }); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java index e1cfd7dba..0e561718d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java @@ -2,10 +2,15 @@ package org.briarproject.briar.android.contact.add.remote; import android.arch.lifecycle.ViewModelProvider; import android.arch.lifecycle.ViewModelProviders; +import android.content.Context; +import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.support.annotation.StringRes; import android.support.design.widget.TextInputEditText; import android.support.design.widget.TextInputLayout; +import android.support.v7.app.AlertDialog.Builder; import android.text.Editable; import android.view.LayoutInflater; import android.view.View; @@ -15,6 +20,10 @@ import android.widget.ProgressBar; import android.widget.Toast; import org.briarproject.bramble.api.UnsupportedVersionException; +import org.briarproject.bramble.api.contact.PendingContact; +import org.briarproject.bramble.api.db.ContactExistsException; +import org.briarproject.bramble.api.db.PendingContactExistsException; +import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; @@ -24,9 +33,13 @@ import org.briarproject.briar.android.fragment.BaseFragment; import javax.annotation.Nullable; import javax.inject.Inject; +import static android.support.v4.content.ContextCompat.getColor; +import static android.support.v4.content.ContextCompat.getDrawable; +import static android.support.v4.graphics.drawable.DrawableCompat.setTint; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_LONG; +import static java.util.Objects.requireNonNull; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong; @@ -107,23 +120,93 @@ public class NicknameFragment extends BaseFragment { viewModel.getAddContactResult().observe(this, result -> { if (result == null) return; - if (result.hasError()) { - int stringRes; - if (result - .getException() instanceof UnsupportedVersionException) { - stringRes = R.string.unsupported_link; - } else { - stringRes = R.string.adding_contact_error; - } - Toast.makeText(getContext(), stringRes, LENGTH_LONG).show(); - } else { - Intent intent = new Intent(getActivity(), - PendingContactListActivity.class); - startActivity(intent); - } - finish(); + if (result.hasError()) handleException(name, result.getException()); + else showPendingContactListActivity(); }); viewModel.addContact(name); } + private void showPendingContactListActivity() { + Intent intent = new Intent(getActivity(), + PendingContactListActivity.class); + startActivity(intent); + finish(); + } + + private void handleException(String name, @Nullable Exception e) { + if (e instanceof ContactExistsException) { + ContactExistsException ce = (ContactExistsException) e; + handleExistingContact(name, ce.getRemoteAuthor()); + } else if (e instanceof PendingContactExistsException) { + PendingContactExistsException pe = + (PendingContactExistsException) e; + handleExistingPendingContact(name, pe.getPendingContact()); + } else if (e instanceof UnsupportedVersionException) { + int stringRes = R.string.unsupported_link; + Toast.makeText(getContext(), stringRes, LENGTH_LONG).show(); + finish(); + } else { + int stringRes = R.string.adding_contact_error; + Toast.makeText(getContext(), stringRes, LENGTH_LONG).show(); + finish(); + } + } + + private void handleExistingContact(String name, Author existing) { + OnClickListener listener = (d, w) -> { + d.dismiss(); + String str = getString(R.string.contact_already_exists, name); + Toast.makeText(getContext(), str, LENGTH_LONG).show(); + finish(); + }; + showSameLinkDialog(existing.getName(), name, + R.string.duplicate_link_dialog_text_1_contact, listener); + } + + private void handleExistingPendingContact(String name, PendingContact p) { + OnClickListener listener = (d, w) -> { + viewModel.updatePendingContact(name, p); + Toast.makeText(getContext(), R.string.pending_contact_updated_toast, + LENGTH_LONG).show(); + d.dismiss(); + showPendingContactListActivity(); + }; + showSameLinkDialog(p.getAlias(), name, + R.string.duplicate_link_dialog_text_1, listener); + } + + private void showSameLinkDialog(String name1, String name2, + @StringRes int existsRes, OnClickListener samePersonListener) { + Context ctx = requireContext(); + Builder b = new Builder(ctx, R.style.BriarDialogTheme_Neutral); + b.setTitle(getString(R.string.duplicate_link_dialog_title)); + String msg = getString(existsRes, name1) + "\n\n" + + getString(R.string.duplicate_link_dialog_text_2, name2, name1); + b.setMessage(msg); + b.setPositiveButton(R.string.same_person_button, samePersonListener); + b.setNegativeButton(R.string.different_person_button, (d, w) -> { + d.dismiss(); + showWarningDialog(name1, name2); + }); + b.setCancelable(false); + b.show(); + } + + private void showWarningDialog(String name1, String name2) { + Context ctx = requireContext(); + Builder b = new Builder(ctx, R.style.BriarDialogTheme); + Drawable icon = getDrawable(ctx, R.drawable.alerts_and_states_error); + setTint(requireNonNull(icon), getColor(ctx, R.color.color_primary)); + b.setIcon(icon); + b.setTitle(getString(R.string.duplicate_link_dialog_title)); + b.setMessage( + getString(R.string.duplicate_link_dialog_text_3, name1, name2)); + b.setPositiveButton(R.string.ok, (d, w) -> { + d.dismiss(); + finish(); + }); + b.setCancelable(false); + b.show(); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java index 7372ae2ae..56ef169b0 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java @@ -62,8 +62,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity { @UiThread private void contactExchangeSucceeded(Author remoteAuthor) { String contactName = remoteAuthor.getName(); - String format = getString(R.string.contact_added_toast); - String text = String.format(format, contactName); + String text = getString(R.string.contact_added_toast, contactName); Toast.makeText(this, text, LENGTH_LONG).show(); supportFinishAfterTransition(); } diff --git a/briar-android/src/main/res/values-zh-rCN/strings.xml b/briar-android/src/main/res/values-zh-rCN/strings.xml index fb3e7d700..e171fa797 100644 --- a/briar-android/src/main/res/values-zh-rCN/strings.xml +++ b/briar-android/src/main/res/values-zh-rCN/strings.xml @@ -199,7 +199,7 @@ 添加此联系人比通常花费了更多时间。\n\n请检查您的联系人是否收到您的链接并已添加您: 无网络连接 重复的链接 - 您已经有此链接的待处理联系人: + 您已经有此链接的待处理联系人:%s %s 和 %s 是同一个人吗? Are %s and %s the same person?