This addresses two types of introduction corner cases:

* force decline when two of our own identities are introduced to each
  other
* throw away introduction requests to the same identity
  (impossible to trigger from UI)

Closes #284
This commit is contained in:
Torsten Grote
2016-04-13 13:46:34 -03:00
parent 9f4cacd81c
commit d0036deaf7
11 changed files with 72 additions and 17 deletions

View File

@@ -36,7 +36,7 @@
android:layout_marginTop="@dimen/message_bubble_timestamp_margin" android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:layout_alignEnd="@+id/introductionText" android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText" android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/acceptButton" android:layout_below="@+id/declineButton"
android:textColor="@color/private_message_date" android:textColor="@color/private_message_date"
android:textSize="@dimen/text_size_tiny" android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24, 13:37"/> tools:text="Dec 24, 13:37"/>
@@ -46,7 +46,6 @@
style="@style/BriarButtonFlat.Positive" style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="-15dp"
android:layout_alignEnd="@+id/introductionText" android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText" android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/introductionText" android:layout_below="@+id/introductionText"
@@ -57,6 +56,7 @@
style="@style/BriarButtonFlat.Negative" style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="-15dp"
android:layout_below="@+id/introductionText" android:layout_below="@+id/introductionText"
android:layout_toLeftOf="@+id/acceptButton" android:layout_toLeftOf="@+id/acceptButton"
android:layout_toStartOf="@+id/acceptButton" android:layout_toStartOf="@+id/acceptButton"

View File

@@ -155,6 +155,7 @@
<string name="introduction_request_sent">You have asked to introduce %1$s to %2$s.</string> <string name="introduction_request_sent">You have asked to introduce %1$s to %2$s.</string>
<string name="introduction_request_received">%1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list?</string> <string name="introduction_request_received">%1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list?</string>
<string name="introduction_request_exists_received">%1$s has asked to introduce you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond:</string> <string name="introduction_request_exists_received">%1$s has asked to introduce you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond:</string>
<string name="introduction_request_for_our_identity_received">%1$s has asked to introduce you to %2$s, but %2$s is one of your other identities. For that reason you cannot accept the introduction:</string>
<string name="introduction_request_answered_received">%1$s has asked to introduce you to %2$s.</string> <string name="introduction_request_answered_received">%1$s has asked to introduce you to %2$s.</string>
<string name="introduction_response_accepted_sent">You accepted the introduction to %1$s.</string> <string name="introduction_response_accepted_sent">You accepted the introduction to %1$s.</string>
<string name="introduction_response_declined_sent">You declined the introduction to %1$s.</string> <string name="introduction_response_declined_sent">You declined the introduction to %1$s.</string>

View File

@@ -261,15 +261,23 @@ class ConversationAdapter extends RecyclerView.Adapter {
contactName, ir.getName())); contactName, ir.getName()));
} }
ui.acceptButton.setVisibility(View.VISIBLE); if (item.getIntroductionRequest().doesIntroduceOtherIdentity()) {
ui.acceptButton.setOnClickListener(new View.OnClickListener() { // don't allow accept when one of our identities is introduced
@Override ui.acceptButton.setVisibility(View.GONE);
public void onClick(View v) { ui.text.setText(ctx.getString(
intro.respondToIntroduction(ir.getSessionId(), true); R.string.introduction_request_for_our_identity_received,
item.setAnswered(true); contactName, ir.getName()));
notifyItemChanged(position); } else {
} ui.acceptButton.setVisibility(View.VISIBLE);
}); ui.acceptButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intro.respondToIntroduction(ir.getSessionId(), true);
item.setAnswered(true);
notifyItemChanged(position);
}
});
}
ui.declineButton.setVisibility(View.VISIBLE); ui.declineButton.setVisibility(View.VISIBLE);
ui.declineButton.setOnClickListener(new View.OnClickListener() { ui.declineButton.setOnClickListener(new View.OnClickListener() {
@Override @Override

View File

@@ -102,6 +102,12 @@ public interface DatabaseComponent {
*/ */
boolean containsGroup(Transaction txn, GroupId g) throws DbException; boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given local author.
*/
boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException;
/** /**
* Deletes the message with the given ID. The message ID and any other * Deletes the message with the given ID. The message ID and any other
* associated data are not deleted. * associated data are not deleted.

View File

@@ -57,6 +57,7 @@ public interface IntroductionConstants {
String ADDED_CONTACT_ID = "addedContactId"; String ADDED_CONTACT_ID = "addedContactId";
String NOT_OUR_RESPONSE = "notOurResponse"; String NOT_OUR_RESPONSE = "notOurResponse";
String EXISTS = "contactExists"; String EXISTS = "contactExists";
String REMOTE_AUTHOR_IS_US = "remoteAuthorIsUs";
String ANSWERED = "answered"; String ANSWERED = "answered";
String TASK = "task"; String TASK = "task";

View File

@@ -6,12 +6,12 @@ import org.briarproject.api.sync.MessageId;
public class IntroductionRequest extends IntroductionResponse { public class IntroductionRequest extends IntroductionResponse {
private final String message; private final String message;
private final boolean answered, exists; private final boolean answered, exists, introducesOtherIdentity;
public IntroductionRequest(SessionId sessionId, MessageId messageId, public IntroductionRequest(SessionId sessionId, MessageId messageId,
long time, boolean local, boolean sent, boolean seen, boolean read, long time, boolean local, boolean sent, boolean seen, boolean read,
AuthorId authorId, String name, boolean accepted, String message, AuthorId authorId, String name, boolean accepted, String message,
boolean answered, boolean exists) { boolean answered, boolean exists, boolean introducesOtherIdentity) {
super(sessionId, messageId, time, local, sent, seen, read, authorId, super(sessionId, messageId, time, local, sent, seen, read, authorId,
name, accepted); name, accepted);
@@ -19,6 +19,7 @@ public class IntroductionRequest extends IntroductionResponse {
this.message = message; this.message = message;
this.answered = answered; this.answered = answered;
this.exists = exists; this.exists = exists;
this.introducesOtherIdentity = introducesOtherIdentity;
} }
public String getMessage() { public String getMessage() {
@@ -33,4 +34,7 @@ public class IntroductionRequest extends IntroductionResponse {
return exists; return exists;
} }
public boolean doesIntroduceOtherIdentity() {
return introducesOtherIdentity;
}
} }

View File

@@ -243,6 +243,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsGroup(txn, g); return db.containsGroup(txn, g);
} }
@Override
public boolean containsLocalAuthor(Transaction transaction, AuthorId local)
throws DbException {
T txn = unbox(transaction);
return db.containsLocalAuthor(txn, local);
}
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();

View File

@@ -47,6 +47,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.OUR_PUBLIC
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME; import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.STATE; import static org.briarproject.api.introduction.IntroductionConstants.STATE;
import static org.briarproject.api.introduction.IntroductionConstants.TASK; import static org.briarproject.api.introduction.IntroductionConstants.TASK;
@@ -328,10 +329,12 @@ public class IntroduceeEngine
String name = msg.getString(NAME); String name = msg.getString(NAME);
String message = msg.getOptionalString(MSG); String message = msg.getOptionalString(MSG);
boolean exists = localState.getBoolean(EXISTS); boolean exists = localState.getBoolean(EXISTS);
boolean introducesOtherIdentity =
localState.getBoolean(REMOTE_AUTHOR_IS_US);
IntroductionRequest ir = new IntroductionRequest(sessionId, messageId, IntroductionRequest ir = new IntroductionRequest(sessionId, messageId,
time, false, false, false, false, authorId, name, false, time, false, false, false, false, authorId, name, false,
message, false, exists); message, false, exists, introducesOtherIdentity);
return new IntroductionRequestReceivedEvent(contactId, ir); return new IntroductionRequestReceivedEvent(contactId, ir);
} }

View File

@@ -59,6 +59,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.OUR_PUBLIC
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME; import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE; import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE; import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
import static org.briarproject.api.introduction.IntroductionConstants.STATE; import static org.briarproject.api.introduction.IntroductionConstants.STATE;
@@ -147,6 +148,17 @@ class IntroduceeManager {
d.put(EXISTS, exists); d.put(EXISTS, exists);
d.put(REMOTE_AUTHOR_ID, remoteAuthorId); d.put(REMOTE_AUTHOR_ID, remoteAuthorId);
// check if someone is trying to introduce us to ourselves
if(remoteAuthorId.equals(introducer.getLocalAuthorId())) {
LOG.warning("Received Introduction Request to Ourselves");
throw new FormatException();
}
// check if remote author is actually one of our other identities
boolean introducesOtherIdentity =
db.containsLocalAuthor(txn, remoteAuthorId);
d.put(REMOTE_AUTHOR_IS_US, introducesOtherIdentity);
// save local state to database // save local state to database
clientHelper.addLocalMessage(txn, localMsg, clientHelper.addLocalMessage(txn, localMsg,
IntroductionManagerImpl.CLIENT_ID, d, false); IntroductionManagerImpl.CLIENT_ID, d, false);

View File

@@ -60,6 +60,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
import static org.briarproject.api.introduction.IntroductionConstants.READ; import static org.briarproject.api.introduction.IntroductionConstants.READ;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1; import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1;
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_2; import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_2;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE; import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
@@ -186,9 +187,11 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
state = introduceeManager.initialize(txn, groupId, message); state = introduceeManager.initialize(txn, groupId, message);
} catch (FormatException e) { } catch (FormatException e) {
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING)) {
LOG.warning("Could not initialize introducee state"); LOG.warning(
"Could not initialize introducee state, deleting...");
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);
} }
deleteMessage(txn, m.getId());
return; return;
} }
try { try {
@@ -361,7 +364,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
list.add(ir); list.add(ir);
} else if (type == TYPE_REQUEST) { } else if (type == TYPE_REQUEST) {
String message; String message;
boolean answered, exists; boolean answered, exists, introducesOtherIdentity;
if (state.getLong(ROLE) == ROLE_INTRODUCER) { if (state.getLong(ROLE) == ROLE_INTRODUCER) {
local = true; local = true;
authorId = authorId =
@@ -370,6 +373,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
message = msg.getOptionalString(MSG); message = msg.getOptionalString(MSG);
answered = false; answered = false;
exists = false; exists = false;
introducesOtherIdentity = false;
} else { } else {
local = false; local = false;
authorId = new AuthorId( authorId = new AuthorId(
@@ -378,11 +382,14 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
message = state.getOptionalString(MSG); message = state.getOptionalString(MSG);
answered = state.getBoolean(ANSWERED); answered = state.getBoolean(ANSWERED);
exists = state.getBoolean(EXISTS); exists = state.getBoolean(EXISTS);
introducesOtherIdentity =
state.getBoolean(REMOTE_AUTHOR_IS_US);
} }
IntroductionRequest ir = new IntroductionRequest( IntroductionRequest ir = new IntroductionRequest(
sessionId, messageId, time, local, s.isSent(), sessionId, messageId, time, local, s.isSent(),
s.isSeen(), read, authorId, name, accepted, s.isSeen(), read, authorId, name, accepted,
message, answered, exists); message, answered, exists,
introducesOtherIdentity);
list.add(ir); list.add(ir);
} }
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -51,6 +51,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE; import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE; import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
@@ -239,6 +240,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
state.put(ANSWERED, false); state.put(ANSWERED, false);
state.put(EXISTS, true); state.put(EXISTS, true);
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId()); state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
state.put(REMOTE_AUTHOR_IS_US, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
@@ -268,6 +270,10 @@ public class IntroduceeManagerTest extends BriarTestCase {
introducer.getLocalAuthorId()); introducer.getLocalAuthorId());
will(returnValue(contactExists)); will(returnValue(contactExists));
// checks if remote author is one of our identities
oneOf(db).containsLocalAuthor(txn, introducee2.getAuthor().getId());
will(returnValue(false));
// store session state // store session state
oneOf(clientHelper) oneOf(clientHelper)
.addLocalMessage(txn, localStateMessage, clientId, state, .addLocalMessage(txn, localStateMessage, clientId, state,