Merge branch '1570-convert-pending-contact' into 'master'

Add database methods for converting a pending contact

Closes #1570

See merge request briar/briar!1107
This commit is contained in:
Torsten Grote
2019-05-27 17:45:26 +00:00
18 changed files with 282 additions and 86 deletions

View File

@@ -32,12 +32,23 @@ public interface ContactManager {
* derives and stores transport keys for each transport, and returns an ID * derives and stores transport keys for each transport, and returns an ID
* for the contact. * for the contact.
* *
* @param alice true if the local party is Alice * @param alice True if the local party is Alice
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey rootKey, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException; boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* replacing the given pending contact, derives and stores transport keys
* for each transport, and returns an ID for the contact.
*
* @param alice True if the local party is Alice
*/
ContactId addContact(Transaction txn, PendingContactId p, Author remote,
AuthorId local, SecretKey rootKey, long timestamp, boolean alice,
boolean verified, boolean active) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms * Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact. * and returns an ID for the contact.
@@ -50,7 +61,7 @@ public interface ContactManager {
* derives and stores transport keys for each transport, and returns an ID * derives and stores transport keys for each transport, and returns an ID
* for the contact. * for the contact.
* *
* @param alice true if the local party is Alice * @param alice True if the local party is Alice
*/ */
ContactId addContact(Author remote, AuthorId local, SecretKey rootKey, ContactId addContact(Author remote, AuthorId local, SecretKey rootKey,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
@@ -132,13 +143,13 @@ public interface ContactManager {
void removeContact(Transaction txn, ContactId c) throws DbException; void removeContact(Transaction txn, ContactId c) throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias for the contact or unsets it if alias is null.
*/ */
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias) void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
throws DbException; throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias for the contact or unsets it if alias is null.
*/ */
void setContactAlias(ContactId c, @Nullable String alias) void setContactAlias(ContactId c, @Nullable String alias)
throws DbException; throws DbException;

View File

@@ -14,12 +14,18 @@ import javax.annotation.concurrent.Immutable;
public class ContactAddedEvent extends Event { public class ContactAddedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final boolean verified;
public ContactAddedEvent(ContactId contactId) { public ContactAddedEvent(ContactId contactId, boolean verified) {
this.contactId = contactId; this.contactId = contactId;
this.verified = verified;
} }
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
public boolean isVerified() {
return verified;
}
} }

View File

@@ -1,22 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ContactAddedRemotelyEvent extends Event {
private final Contact contact;
public ContactAddedRemotelyEvent(Contact contact) {
this.contact = contact;
}
public Contact getContact() {
return contact;
}
}

View File

@@ -63,6 +63,13 @@ public interface DatabaseComponent extends TransactionManager {
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException; boolean verified) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* replacing the given pending contact, and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, PendingContactId p, Author remote,
AuthorId local, boolean verified) throws DbException;
/** /**
* Stores a group. * Stores a group.
*/ */

View File

@@ -81,6 +81,19 @@ class ContactManagerImpl implements ContactManager {
return c; return c;
} }
@Override
public ContactId addContact(Transaction txn, PendingContactId p,
Author remote, AuthorId local, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active)
throws DbException {
ContactId c = db.addContact(txn, p, remote, local, verified);
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException { boolean verified) throws DbException {

View File

@@ -630,7 +630,8 @@ interface Database<T> {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(T txn, TransportId t, KeySetId k) throws DbException; void removeTransportKeys(T txn, TransportId t, KeySetId k)
throws DbException;
/** /**
* Resets the transmission count and expiry time of the given message with * Resets the transmission count and expiry time of the given message with
@@ -686,6 +687,14 @@ interface Database<T> {
void setTransportKeysActive(T txn, TransportId t, KeySetId k) void setTransportKeysActive(T txn, TransportId t, KeySetId k)
throws DbException; throws DbException;
/**
* Transfers ownership of any transport keys from the given pending contact
* to the given contact and copies the pending contact's handshake public
* key to the contact.
*/
void transferKeys(T txn, PendingContactId p, ContactId c)
throws DbException;
/** /**
* Updates the transmission count, expiry time and estimated time of arrival * Updates the transmission count, expiry time and estimated time of arrival
* of the given message with respect to the given contact, using the latency * of the given message with respect to the given contact, using the latency

View File

@@ -244,7 +244,29 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (db.containsContact(txn, remote.getId(), local)) if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(local, remote); throw new ContactExistsException(local, remote);
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, remote, local, verified);
transaction.attach(new ContactAddedEvent(c)); transaction.attach(new ContactAddedEvent(c, verified));
return c;
}
@Override
public ContactId addContact(Transaction transaction, PendingContactId p,
Author remote, AuthorId local, boolean verified)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsIdentity(txn, local))
throw new NoSuchIdentityException();
if (db.containsIdentity(txn, remote.getId()))
throw new ContactExistsException(local, remote);
if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(local, remote);
ContactId c = db.addContact(txn, remote, local, verified);
db.transferKeys(txn, p, c);
db.removePendingContact(txn, p);
transaction.attach(new ContactAddedEvent(c, verified));
transaction.attach(new PendingContactRemovedEvent(p));
return c; return c;
} }

View File

@@ -3115,6 +3115,48 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void transferKeys(Connection txn, PendingContactId p, ContactId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Transfer the handshake public key
String sql = "SELECT publicKey from pendingContacts"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
byte[] publicKey = rs.getBytes(1);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
sql = "UPDATE contacts SET handshakePublicKey = ?"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, publicKey);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Transfer the transport keys
sql = "UPDATE outgoingKeys"
+ " SET contactId = ?, pendingContactId = NULL"
+ " WHERE pendingContactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, p.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m, public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
int maxLatency) throws DbException { int maxLatency) throws DbException {

View File

@@ -763,16 +763,25 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the pending contact is in the DB (which it's not) // Check whether the pending contact is in the DB (which it's not)
exactly(2).of(database).startTransaction(); exactly(3).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(2).of(database).containsPendingContact(txn, exactly(3).of(database).containsPendingContact(txn,
pendingContactId); pendingContactId);
will(returnValue(false)); will(returnValue(false));
exactly(2).of(database).abortTransaction(txn); exactly(3).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addTransportKeys(transaction, pendingContactId, db.addTransportKeys(transaction, pendingContactId,
@@ -1494,6 +1503,69 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
} }
@Test
public void testCannotAddLocalIdentityAsContactFromPendingContact()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsPendingContact(txn, pendingContactId);
will(returnValue(true));
oneOf(database).containsIdentity(txn, localAuthor.getId());
will(returnValue(true));
// Contact is a local identity
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(true));
oneOf(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail();
} catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
assertEquals(author, expected.getRemoteAuthor());
}
}
@Test
public void testCannotAddDuplicateContactFromPendingContact()
throws Exception {
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsPendingContact(txn, pendingContactId);
will(returnValue(true));
oneOf(database).containsIdentity(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(false));
// Contact already exists for this local identity
oneOf(database).containsContact(txn, author.getId(),
localAuthor.getId());
will(returnValue(true));
oneOf(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, pendingContactId, author,
localAuthor.getId(), true));
fail();
} catch (ContactExistsException expected) {
assertEquals(localAuthor.getId(), expected.getLocalAuthorId());
assertEquals(author, expected.getRemoteAuthor());
}
}
@Test @Test
public void testMessageDependencies() throws Exception { public void testMessageDependencies() throws Exception {
int shutdownHandle = 12345; int shutdownHandle = 12345;

View File

@@ -2204,12 +2204,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(emptyList(), db.getPendingContacts(txn)); assertEquals(emptyList(), db.getPendingContacts(txn));
db.addPendingContact(txn, pendingContact); db.addPendingContact(txn, pendingContact);
Collection<PendingContact> pendingContacts = Collection<PendingContact> pendingContacts = db.getPendingContacts(txn);
db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size()); assertEquals(1, pendingContacts.size());
PendingContact retrieved = pendingContacts.iterator().next(); PendingContact retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId()); assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias()); assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertArrayEquals(pendingContact.getPublicKey().getEncoded(),
retrieved.getPublicKey().getEncoded());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp()); assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.removePendingContact(txn, pendingContact.getId()); db.removePendingContact(txn, pendingContact.getId());
@@ -2219,6 +2220,60 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testTransferKeys() throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys =
createHandshakeKeys(1000, getSecretKey(), alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the pending contact, the transport and the handshake keys
db.addPendingContact(txn, pendingContact);
db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, pendingContact.getId(),
transportKeys));
Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, allKeys.size());
TransportKeySet ks = allKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
// Add a contact
db.addIdentity(txn, identity);
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
// The contact shouldn't have a handshake public key
Contact contact = db.getContact(txn, contactId);
assertNull(contact.getHandshakePublicKey());
// Transfer the keys to the contact
db.transferKeys(txn, pendingContact.getId(), contactId);
// The handshake public key should have been copied to the contact
contact = db.getContact(txn, contactId);
PublicKey handshakePublicKey = contact.getHandshakePublicKey();
assertNotNull(handshakePublicKey);
assertArrayEquals(pendingContact.getPublicKey().getEncoded(),
handshakePublicKey.getEncoded());
// The transport keys should have been transferred to the contact
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(1, allKeys.size());
ks = allKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testSetHandshakeKeyPair() throws Exception { public void testSetHandshakeKeyPair() throws Exception {
Identity withoutKeys = new Identity(localAuthor, null, null, Identity withoutKeys = new Identity(localAuthor, null, null,

View File

@@ -153,7 +153,7 @@ public class PollerImplTest extends BrambleMockTestCase {
will(returnValue(false)); will(returnValue(false));
}}); }});
poller.eventOccurred(new ContactAddedEvent(contactId)); poller.eventOccurred(new ContactAddedEvent(contactId, true));
} }
@Test @Test

View File

@@ -16,6 +16,7 @@ import android.support.v4.app.TaskStackBuilder;
import org.briarproject.bramble.api.Multiset; import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
@@ -42,7 +43,6 @@ import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent; import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent; import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent; import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent; import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent;
import java.util.Set; import java.util.Set;
@@ -230,8 +230,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} else if (e instanceof BlogPostAddedEvent) { } else if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e; BlogPostAddedEvent b = (BlogPostAddedEvent) e;
if (!b.isLocal()) showBlogPostNotification(b.getGroupId()); if (!b.isLocal()) showBlogPostNotification(b.getGroupId());
} else if (e instanceof ContactAddedRemotelyEvent) { } else if (e instanceof ContactAddedEvent) {
showContactAddedNotification(); ContactAddedEvent c = (ContactAddedEvent) e;
// Don't show notifications for contacts added in person
if (!c.isVerified()) showContactAddedNotification();
} }
} }

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -453,10 +452,6 @@ class IntroduceeProtocolEngine
//noinspection ConstantConditions //noinspection ConstantConditions
transportPropertyManager.addRemoteProperties(txn, c.getId(), transportPropertyManager.addRemoteProperties(txn, c.getId(),
s.getRemote().transportProperties); s.getRemote().transportProperties);
// Broadcast IntroductionSucceededEvent, because contact got added
ContactAddedRemotelyEvent e = new ContactAddedRemotelyEvent(c);
txn.attach(e);
} catch (ContactExistsException e) { } catch (ContactExistsException e) {
// Ignore this, because the other introducee might have deleted us. // Ignore this, because the other introducee might have deleted us.
// So we still want updated transport properties // So we still want updated transport properties

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
@@ -32,7 +33,6 @@ import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent; import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent; import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent; import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.briar.test.BriarIntegrationTest; import org.briarproject.briar.test.BriarIntegrationTest;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -1217,7 +1217,6 @@ public class IntroductionIntegrationTest
volatile boolean aborted = false; volatile boolean aborted = false;
volatile Event latestEvent; volatile Event latestEvent;
@SuppressWarnings("WeakerAccess")
IntroductionResponse getResponse() { IntroductionResponse getResponse() {
assertTrue( assertTrue(
latestEvent instanceof IntroductionResponseReceivedEvent); latestEvent instanceof IntroductionResponseReceivedEvent);
@@ -1273,12 +1272,11 @@ public class IntroductionIntegrationTest
// only broadcast for DECLINE messages in introducee role // only broadcast for DECLINE messages in introducee role
latestEvent = e; latestEvent = e;
eventWaiter.resume(); eventWaiter.resume();
} else if (e instanceof ContactAddedRemotelyEvent) { } else if (e instanceof ContactAddedEvent) {
latestEvent = e; latestEvent = e;
succeeded = true; succeeded = true;
Contact contact = ((ContactAddedRemotelyEvent) e).getContact(); ContactId contactId = ((ContactAddedEvent) e).getContactId();
eventWaiter eventWaiter.assertFalse(contactId.equals(contactId0From1));
.assertFalse(contact.getId().equals(contactId0From1));
eventWaiter.resume(); eventWaiter.resume();
} else if (e instanceof IntroductionAbortedEvent) { } else if (e instanceof IntroductionAbortedEvent) {
latestEvent = e; latestEvent = e;
@@ -1357,13 +1355,10 @@ public class IntroductionIntegrationTest
Message m = ch.getMessage(id); Message m = ch.getMessage(id);
BdfList body = ch.getMessageAsList(id); BdfList body = ch.getMessageAsList(id);
if (type == ACCEPT) { if (type == ACCEPT) {
//noinspection ConstantConditions
return c0.getMessageParser().parseAcceptMessage(m, body); return c0.getMessageParser().parseAcceptMessage(m, body);
} else if (type == DECLINE) { } else if (type == DECLINE) {
//noinspection ConstantConditions
return c0.getMessageParser().parseDeclineMessage(m, body); return c0.getMessageParser().parseDeclineMessage(m, body);
} else if (type == AUTH) { } else if (type == AUTH) {
//noinspection ConstantConditions
return c0.getMessageParser().parseAuthMessage(m, body); return c0.getMessageParser().parseAuthMessage(m, body);
} else throw new AssertionError("Not implemented"); } else throw new AssertionError("Not implemented");
} }

View File

@@ -143,7 +143,7 @@ The following events are relevant here:
* `PendingContactAddedEvent` * `PendingContactAddedEvent`
* `PendingContactStateChangedEvent` * `PendingContactStateChangedEvent`
* `PendingContactRemovedEvent` * `PendingContactRemovedEvent`
* `ContactAddedRemotelyEvent` (when the pending contact becomes an actual contact) * `ContactAddedEvent` (when the pending contact becomes an actual contact)
To remove a pending contact and abort the process of adding it: To remove a pending contact and abort the process of adding it:
@@ -287,28 +287,15 @@ it will send a JSON object to connected websocket clients:
Note that the JSON object in `data` is exactly what the REST API returns Note that the JSON object in `data` is exactly what the REST API returns
when listing private messages. when listing private messages.
### A new contact was added remotely ### A new contact was added
When the Briar peer adds a new contact remotely,
it will send a JSON object representing the new contact to connected websocket clients:
```json ```json
{ {
"data": { "data": {
"contact": { "contactId": 1,
"author": { "verified": false
"formatVersion": 1,
"id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=",
"name": "Test",
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
},
"contactId": 1,
"alias" : "A local nickname",
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
"verified": true
}
}, },
"name": "ContactAddedRemotelyEvent", "name": "ContactAddedEvent",
"type": "event" "type": "event"
} }
``` ```
@@ -334,8 +321,8 @@ it will send a JSON object representing the new contact to connected websocket c
```json ```json
{ {
"data": { "data": {
"pendingContactId":"YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M=", "pendingContactId": "YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M=",
"state":"waiting_for_connection" "state": "waiting_for_connection"
}, },
"name": "PendingContactStateChangedEvent", "name": "PendingContactStateChangedEvent",
"type": "event" "type": "event"

View File

@@ -7,7 +7,7 @@ import io.javalin.NotFoundResponse
import org.briarproject.bramble.api.contact.ContactManager import org.briarproject.bramble.api.contact.ContactManager
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX
import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.PendingContactId
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent import org.briarproject.bramble.api.contact.event.ContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
@@ -27,7 +27,7 @@ import javax.annotation.concurrent.Immutable
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
internal const val EVENT_CONTACT_ADDED_REMOTELY = "ContactAddedRemotelyEvent" internal const val EVENT_CONTACT_ADDED = "ContactAddedEvent"
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent" internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent" internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent"
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent" internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
@@ -43,8 +43,8 @@ constructor(
) : ContactController, EventListener { ) : ContactController, EventListener {
override fun eventOccurred(e: Event) = when (e) { override fun eventOccurred(e: Event) = when (e) {
is ContactAddedRemotelyEvent -> { is ContactAddedEvent -> {
webSocket.sendEvent(EVENT_CONTACT_ADDED_REMOTELY, e.output()) webSocket.sendEvent(EVENT_CONTACT_ADDED, e.output())
} }
is PendingContactStateChangedEvent -> { is PendingContactStateChangedEvent -> {
webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output()) webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output())

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.headless.contact package org.briarproject.briar.headless.contact
import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.contact.Contact
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent import org.briarproject.bramble.api.contact.event.ContactAddedEvent
import org.briarproject.bramble.identity.output import org.briarproject.bramble.identity.output
import org.briarproject.briar.headless.json.JsonDict import org.briarproject.briar.headless.json.JsonDict
@@ -14,6 +14,7 @@ internal fun Contact.output() = JsonDict(
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) } handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
} }
internal fun ContactAddedRemotelyEvent.output() = JsonDict( internal fun ContactAddedEvent.output() = JsonDict(
"contact" to contact.output() "contactId" to contactId.int,
"verified" to isVerified
) )

View File

@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.PendingContactId
import org.briarproject.bramble.api.contact.PendingContactState.FAILED import org.briarproject.bramble.api.contact.PendingContactState.FAILED
import org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION import org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent import org.briarproject.bramble.api.contact.event.ContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
@@ -207,12 +207,12 @@ internal class ContactControllerTest : ControllerTest() {
} }
@Test @Test
fun testContactAddedRemotelyEvent() { fun testContactAddedEvent() {
val event = ContactAddedRemotelyEvent(contact) val event = ContactAddedEvent(contact.id, contact.isVerified)
every { every {
webSocketController.sendEvent( webSocketController.sendEvent(
EVENT_CONTACT_ADDED_REMOTELY, EVENT_CONTACT_ADDED,
event.output() event.output()
) )
} just runs } just runs
@@ -291,11 +291,12 @@ internal class ContactControllerTest : ControllerTest() {
} }
@Test @Test
fun testOutputContactAddedRemotelyEvent() { fun testOutputContactAddedEvent() {
val event = ContactAddedRemotelyEvent(contact) val event = ContactAddedEvent(contact.id, contact.isVerified)
val json = """ val json = """
{ {
"contact": ${toJson(contact.output())} "contactId": ${contact.id.int},
"verified": ${contact.isVerified}
} }
""" """
assertJsonEquals(json, event.output()) assertJsonEquals(json, event.output())