mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Merge branch '276-introduction-tests' into 'master'
Unit Tests for the Introduction Client Please note that I based this MR is on top of the introduction UI in !122, because I needed to do some more refactoring on top of the other refactoring ;) The refactoring and the tests are in two separate commits. An integration test will be added as a separate MR. This addresses part of #276. See merge request !136
This commit is contained in:
@@ -45,22 +45,4 @@ public interface IntroductionManager {
|
|||||||
Collection<IntroductionMessage> getIntroductionMessages(ContactId contactId)
|
Collection<IntroductionMessage> getIntroductionMessages(ContactId contactId)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/** Marks an introduction message as read or unread. */
|
|
||||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
|
||||||
|
|
||||||
|
|
||||||
/** Get the session state for the given session ID */
|
|
||||||
BdfDictionary getSessionState(Transaction txn, GroupId groupId,
|
|
||||||
byte[] sessionId) throws DbException, FormatException;
|
|
||||||
|
|
||||||
/** Gets the group used for introductions with Contact c */
|
|
||||||
Group getIntroductionGroup(Contact c);
|
|
||||||
|
|
||||||
/** Get the local group used to store session states */
|
|
||||||
Group getLocalGroup();
|
|
||||||
|
|
||||||
/** Send an introduction message */
|
|
||||||
void sendMessage(Transaction txn, BdfDictionary message)
|
|
||||||
throws DbException, FormatException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,8 @@ import org.briarproject.api.event.IntroductionSucceededEvent;
|
|||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.AuthorFactory;
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
import org.briarproject.api.introduction.IntroductionManager;
|
|
||||||
import org.briarproject.api.introduction.SessionId;
|
|
||||||
import org.briarproject.api.properties.TransportProperties;
|
import org.briarproject.api.properties.TransportProperties;
|
||||||
import org.briarproject.api.properties.TransportPropertyManager;
|
import org.briarproject.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.api.sync.Group;
|
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
import org.briarproject.api.sync.Message;
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
@@ -40,6 +37,8 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||||
@@ -76,33 +75,36 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPO
|
|||||||
|
|
||||||
class IntroduceeManager {
|
class IntroduceeManager {
|
||||||
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(IntroduceeManager.class.getName());
|
Logger.getLogger(IntroduceeManager.class.getName());
|
||||||
|
|
||||||
|
private final MessageSender messageSender;
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final IntroductionManager introductionManager;
|
|
||||||
private final ClientHelper clientHelper;
|
private final ClientHelper clientHelper;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CryptoComponent cryptoComponent;
|
private final CryptoComponent cryptoComponent;
|
||||||
private final TransportPropertyManager transportPropertyManager;
|
private final TransportPropertyManager transportPropertyManager;
|
||||||
private final AuthorFactory authorFactory;
|
private final AuthorFactory authorFactory;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
|
private final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
|
||||||
IntroduceeManager(DatabaseComponent db,
|
@Inject
|
||||||
IntroductionManager introductionManager, ClientHelper clientHelper,
|
IntroduceeManager(MessageSender messageSender, DatabaseComponent db,
|
||||||
Clock clock, CryptoComponent cryptoComponent,
|
ClientHelper clientHelper, Clock clock,
|
||||||
|
CryptoComponent cryptoComponent,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
AuthorFactory authorFactory, ContactManager contactManager) {
|
AuthorFactory authorFactory, ContactManager contactManager,
|
||||||
|
IntroductionGroupFactory introductionGroupFactory) {
|
||||||
|
|
||||||
|
this.messageSender = messageSender;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.introductionManager = introductionManager;
|
|
||||||
this.clientHelper = clientHelper;
|
this.clientHelper = clientHelper;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.cryptoComponent = cryptoComponent;
|
this.cryptoComponent = cryptoComponent;
|
||||||
this.transportPropertyManager = transportPropertyManager;
|
this.transportPropertyManager = transportPropertyManager;
|
||||||
this.authorFactory = authorFactory;
|
this.authorFactory = authorFactory;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
|
this.introductionGroupFactory = introductionGroupFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary initialize(Transaction txn, GroupId groupId,
|
public BdfDictionary initialize(Transaction txn, GroupId groupId,
|
||||||
@@ -113,9 +115,9 @@ class IntroduceeManager {
|
|||||||
Bytes salt = new Bytes(new byte[64]);
|
Bytes salt = new Bytes(new byte[64]);
|
||||||
cryptoComponent.getSecureRandom().nextBytes(salt.getBytes());
|
cryptoComponent.getSecureRandom().nextBytes(salt.getBytes());
|
||||||
|
|
||||||
Message localMsg = clientHelper
|
Message localMsg = clientHelper.createMessage(
|
||||||
.createMessage(introductionManager.getLocalGroup().getId(), now,
|
introductionGroupFactory.createLocalGroup().getId(), now,
|
||||||
BdfList.of(salt));
|
BdfList.of(salt));
|
||||||
MessageId storageId = localMsg.getId();
|
MessageId storageId = localMsg.getId();
|
||||||
|
|
||||||
// find out who is introducing us
|
// find out who is introducing us
|
||||||
@@ -133,7 +135,7 @@ class IntroduceeManager {
|
|||||||
d.put(INTRODUCER, introducer.getAuthor().getName());
|
d.put(INTRODUCER, introducer.getAuthor().getName());
|
||||||
d.put(CONTACT_ID_1, introducer.getId().getInt());
|
d.put(CONTACT_ID_1, introducer.getId().getInt());
|
||||||
d.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
d.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
||||||
d.put(NOT_OUR_RESPONSE, new byte[0]);
|
d.put(NOT_OUR_RESPONSE, storageId);
|
||||||
d.put(ANSWERED, false);
|
d.put(ANSWERED, false);
|
||||||
|
|
||||||
// check if the contact we are introduced to does already exist
|
// check if the contact we are introduced to does already exist
|
||||||
@@ -147,7 +149,7 @@ class IntroduceeManager {
|
|||||||
|
|
||||||
// save local state to database
|
// save local state to database
|
||||||
clientHelper.addLocalMessage(txn, localMsg,
|
clientHelper.addLocalMessage(txn, localMsg,
|
||||||
introductionManager.getClientId(), d, false);
|
IntroductionManagerImpl.CLIENT_ID, d, false);
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -159,16 +161,10 @@ class IntroduceeManager {
|
|||||||
processStateUpdate(txn, engine.onMessageReceived(state, message));
|
processStateUpdate(txn, engine.onMessageReceived(state, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptIntroduction(Transaction txn, final ContactId contactId,
|
public void acceptIntroduction(Transaction txn, BdfDictionary state,
|
||||||
final SessionId sessionId, final long timestamp)
|
final long timestamp)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
|
|
||||||
Contact c = db.getContact(txn, contactId);
|
|
||||||
Group g = introductionManager.getIntroductionGroup(c);
|
|
||||||
|
|
||||||
BdfDictionary state = introductionManager
|
|
||||||
.getSessionState(txn, g.getId(), sessionId.getBytes());
|
|
||||||
|
|
||||||
// get data to connect and derive a shared secret later
|
// get data to connect and derive a shared secret later
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
|
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
|
||||||
@@ -195,16 +191,10 @@ class IntroduceeManager {
|
|||||||
processStateUpdate(txn, engine.onLocalAction(state, localAction));
|
processStateUpdate(txn, engine.onLocalAction(state, localAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declineIntroduction(Transaction txn, final ContactId contactId,
|
public void declineIntroduction(Transaction txn, BdfDictionary state,
|
||||||
final SessionId sessionId, final long timestamp)
|
final long timestamp)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
|
|
||||||
Contact c = db.getContact(txn, contactId);
|
|
||||||
Group g = introductionManager.getIntroductionGroup(c);
|
|
||||||
|
|
||||||
BdfDictionary state = introductionManager
|
|
||||||
.getSessionState(txn, g.getId(), sessionId.getBytes());
|
|
||||||
|
|
||||||
// update session state
|
// update session state
|
||||||
state.put(ACCEPT, false);
|
state.put(ACCEPT, false);
|
||||||
|
|
||||||
@@ -233,7 +223,7 @@ class IntroduceeManager {
|
|||||||
|
|
||||||
// send messages
|
// send messages
|
||||||
for (BdfDictionary d : result.toSend) {
|
for (BdfDictionary d : result.toSend) {
|
||||||
introductionManager.sendMessage(txn, d);
|
messageSender.sendMessage(txn, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast events
|
// broadcast events
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.briarproject.api.data.BdfList;
|
|||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
import org.briarproject.api.event.Event;
|
import org.briarproject.api.event.Event;
|
||||||
import org.briarproject.api.introduction.IntroductionManager;
|
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.briarproject.api.sync.Message;
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
@@ -20,6 +19,8 @@ import org.briarproject.util.StringUtils;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
|
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||||
@@ -39,7 +40,6 @@ import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRO
|
|||||||
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.STORAGE_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
@@ -49,19 +49,22 @@ class IntroducerManager {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(IntroducerManager.class.getName());
|
Logger.getLogger(IntroducerManager.class.getName());
|
||||||
|
|
||||||
private final IntroductionManager introductionManager;
|
private final MessageSender messageSender;
|
||||||
private final ClientHelper clientHelper;
|
private final ClientHelper clientHelper;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CryptoComponent cryptoComponent;
|
private final CryptoComponent cryptoComponent;
|
||||||
|
private final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
|
||||||
IntroducerManager(IntroductionManager introductionManager,
|
@Inject
|
||||||
ClientHelper clientHelper, Clock clock,
|
IntroducerManager(MessageSender messageSender, ClientHelper clientHelper,
|
||||||
CryptoComponent cryptoComponent) {
|
Clock clock, CryptoComponent cryptoComponent,
|
||||||
|
IntroductionGroupFactory introductionGroupFactory) {
|
||||||
|
|
||||||
this.introductionManager = introductionManager;
|
this.messageSender = messageSender;
|
||||||
this.clientHelper = clientHelper;
|
this.clientHelper = clientHelper;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.cryptoComponent = cryptoComponent;
|
this.cryptoComponent = cryptoComponent;
|
||||||
|
this.introductionGroupFactory = introductionGroupFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary initialize(Transaction txn, Contact c1, Contact c2)
|
public BdfDictionary initialize(Transaction txn, Contact c1, Contact c2)
|
||||||
@@ -72,13 +75,13 @@ class IntroducerManager {
|
|||||||
Bytes salt = new Bytes(new byte[64]);
|
Bytes salt = new Bytes(new byte[64]);
|
||||||
cryptoComponent.getSecureRandom().nextBytes(salt.getBytes());
|
cryptoComponent.getSecureRandom().nextBytes(salt.getBytes());
|
||||||
|
|
||||||
Message m = clientHelper
|
Message m = clientHelper.createMessage(
|
||||||
.createMessage(introductionManager.getLocalGroup().getId(), now,
|
introductionGroupFactory.createLocalGroup().getId(), now,
|
||||||
BdfList.of(salt));
|
BdfList.of(salt));
|
||||||
MessageId sessionId = m.getId();
|
MessageId sessionId = m.getId();
|
||||||
|
|
||||||
Group g1 = introductionManager.getIntroductionGroup(c1);
|
Group g1 = introductionGroupFactory.createIntroductionGroup(c1);
|
||||||
Group g2 = introductionManager.getIntroductionGroup(c2);
|
Group g2 = introductionGroupFactory.createIntroductionGroup(c2);
|
||||||
|
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
d.put(SESSION_ID, sessionId);
|
d.put(SESSION_ID, sessionId);
|
||||||
@@ -95,7 +98,9 @@ class IntroducerManager {
|
|||||||
d.put(AUTHOR_ID_2, c2.getAuthor().getId());
|
d.put(AUTHOR_ID_2, c2.getAuthor().getId());
|
||||||
|
|
||||||
// save local state to database
|
// save local state to database
|
||||||
clientHelper.addLocalMessage(txn, m, introductionManager.getClientId(), d, false);
|
clientHelper
|
||||||
|
.addLocalMessage(txn, m, IntroductionManagerImpl.CLIENT_ID, d,
|
||||||
|
false);
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -143,7 +148,7 @@ class IntroducerManager {
|
|||||||
|
|
||||||
// send messages
|
// send messages
|
||||||
for (BdfDictionary d : result.toSend) {
|
for (BdfDictionary d : result.toSend) {
|
||||||
introductionManager.sendMessage(txn, d);
|
messageSender.sendMessage(txn, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast events
|
// broadcast events
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
class IntroductionGroupFactory {
|
||||||
|
|
||||||
|
final private PrivateGroupFactory privateGroupFactory;
|
||||||
|
final private Group localGroup;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
IntroductionGroupFactory(PrivateGroupFactory privateGroupFactory) {
|
||||||
|
this.privateGroupFactory = privateGroupFactory;
|
||||||
|
localGroup = privateGroupFactory
|
||||||
|
.createLocalGroup(IntroductionManagerImpl.CLIENT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group createIntroductionGroup(Contact c) {
|
||||||
|
return privateGroupFactory
|
||||||
|
.createPrivateGroup(IntroductionManagerImpl.CLIENT_ID, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group createLocalGroup() {
|
||||||
|
return localGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,25 +3,17 @@ package org.briarproject.introduction;
|
|||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.clients.Client;
|
import org.briarproject.api.clients.Client;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.MessageQueueManager;
|
|
||||||
import org.briarproject.api.clients.PrivateGroupFactory;
|
|
||||||
import org.briarproject.api.contact.Contact;
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.contact.ContactId;
|
import org.briarproject.api.contact.ContactId;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
|
||||||
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||||
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfEntry;
|
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
|
||||||
import org.briarproject.api.data.MetadataParser;
|
import org.briarproject.api.data.MetadataParser;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Metadata;
|
|
||||||
import org.briarproject.api.db.NoSuchMessageException;
|
import org.briarproject.api.db.NoSuchMessageException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
import org.briarproject.api.identity.AuthorFactory;
|
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||||
import org.briarproject.api.introduction.IntroductionManager;
|
import org.briarproject.api.introduction.IntroductionManager;
|
||||||
@@ -29,7 +21,6 @@ import org.briarproject.api.introduction.IntroductionMessage;
|
|||||||
import org.briarproject.api.introduction.IntroductionRequest;
|
import org.briarproject.api.introduction.IntroductionRequest;
|
||||||
import org.briarproject.api.introduction.IntroductionResponse;
|
import org.briarproject.api.introduction.IntroductionResponse;
|
||||||
import org.briarproject.api.introduction.SessionId;
|
import org.briarproject.api.introduction.SessionId;
|
||||||
import org.briarproject.api.properties.TransportPropertyManager;
|
|
||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
@@ -94,37 +85,22 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
Logger.getLogger(IntroductionManagerImpl.class.getName());
|
Logger.getLogger(IntroductionManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final MessageQueueManager messageQueueManager;
|
|
||||||
private final PrivateGroupFactory privateGroupFactory;
|
|
||||||
private final MetadataEncoder metadataEncoder;
|
|
||||||
private final IntroducerManager introducerManager;
|
private final IntroducerManager introducerManager;
|
||||||
private final IntroduceeManager introduceeManager;
|
private final IntroduceeManager introduceeManager;
|
||||||
private final Group localGroup;
|
private final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
IntroductionManagerImpl(DatabaseComponent db,
|
IntroductionManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||||
MessageQueueManager messageQueueManager,
|
MetadataParser metadataParser, Clock clock,
|
||||||
ClientHelper clientHelper, PrivateGroupFactory privateGroupFactory,
|
IntroducerManager introducerManager,
|
||||||
MetadataEncoder metadataEncoder, MetadataParser metadataParser,
|
IntroduceeManager introduceeManager,
|
||||||
CryptoComponent cryptoComponent,
|
IntroductionGroupFactory introductionGroupFactory) {
|
||||||
TransportPropertyManager transportPropertyManager,
|
|
||||||
AuthorFactory authorFactory, ContactManager contactManager,
|
|
||||||
Clock clock) {
|
|
||||||
|
|
||||||
super(clientHelper, metadataParser, clock);
|
super(clientHelper, metadataParser, clock);
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.messageQueueManager = messageQueueManager;
|
this.introducerManager = introducerManager;
|
||||||
this.privateGroupFactory = privateGroupFactory;
|
this.introduceeManager = introduceeManager;
|
||||||
this.metadataEncoder = metadataEncoder;
|
this.introductionGroupFactory = introductionGroupFactory;
|
||||||
// TODO: Inject these dependencies for easier testing
|
|
||||||
this.introducerManager =
|
|
||||||
new IntroducerManager(this, clientHelper, clock,
|
|
||||||
cryptoComponent);
|
|
||||||
this.introduceeManager =
|
|
||||||
new IntroduceeManager(db, this, clientHelper, clock,
|
|
||||||
cryptoComponent, transportPropertyManager,
|
|
||||||
authorFactory, contactManager);
|
|
||||||
localGroup = privateGroupFactory.createLocalGroup(CLIENT_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,7 +110,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createLocalState(Transaction txn) throws DbException {
|
public void createLocalState(Transaction txn) throws DbException {
|
||||||
db.addGroup(txn, localGroup);
|
db.addGroup(txn, introductionGroupFactory.createLocalGroup());
|
||||||
// Ensure we've set things up for any pre-existing contacts
|
// Ensure we've set things up for any pre-existing contacts
|
||||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||||
}
|
}
|
||||||
@@ -143,7 +119,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||||
try {
|
try {
|
||||||
// Create an introduction group for sending introduction messages
|
// Create an introduction group for sending introduction messages
|
||||||
Group g = getIntroductionGroup(c);
|
Group g = introductionGroupFactory.createIntroductionGroup(c);
|
||||||
// Return if we've already set things up for this contact
|
// Return if we've already set things up for this contact
|
||||||
if (db.containsGroup(txn, g.getId())) return;
|
if (db.containsGroup(txn, g.getId())) return;
|
||||||
// Store the group and share it with the contact
|
// Store the group and share it with the contact
|
||||||
@@ -164,7 +140,8 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
Long id = (long) c.getId().getInt();
|
Long id = (long) c.getId().getInt();
|
||||||
try {
|
try {
|
||||||
Map<MessageId, BdfDictionary> map = clientHelper
|
Map<MessageId, BdfDictionary> map = clientHelper
|
||||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
.getMessageMetadataAsDictionary(txn,
|
||||||
|
introductionGroupFactory.createLocalGroup().getId());
|
||||||
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||||
BdfDictionary d = entry.getValue();
|
BdfDictionary d = entry.getValue();
|
||||||
long role = d.getLong(ROLE, -1L);
|
long role = d.getLong(ROLE, -1L);
|
||||||
@@ -185,7 +162,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
|
|
||||||
// remove the group (all messages will be removed with it)
|
// remove the group (all messages will be removed with it)
|
||||||
// this contact won't get our abort message, but the other will
|
// this contact won't get our abort message, but the other will
|
||||||
db.removeGroup(txn, getIntroductionGroup(c));
|
db.removeGroup(txn, introductionGroupFactory.createIntroductionGroup(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,8 +264,12 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
|
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
introduceeManager
|
Contact c = db.getContact(txn, contactId);
|
||||||
.acceptIntroduction(txn, contactId, sessionId, timestamp);
|
Group g = introductionGroupFactory.createIntroductionGroup(c);
|
||||||
|
BdfDictionary state =
|
||||||
|
getSessionState(txn, g.getId(), sessionId.getBytes());
|
||||||
|
|
||||||
|
introduceeManager.acceptIntroduction(txn, state, timestamp);
|
||||||
txn.setComplete();
|
txn.setComplete();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
@@ -302,8 +283,12 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
|
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
introduceeManager
|
Contact c = db.getContact(txn, contactId);
|
||||||
.declineIntroduction(txn, contactId, sessionId, timestamp);
|
Group g = introductionGroupFactory.createIntroductionGroup(c);
|
||||||
|
BdfDictionary state =
|
||||||
|
getSessionState(txn, g.getId(), sessionId.getBytes());
|
||||||
|
|
||||||
|
introduceeManager.declineIntroduction(txn, state, timestamp);
|
||||||
txn.setComplete();
|
txn.setComplete();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
@@ -322,8 +307,9 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
Transaction txn = db.startTransaction(true);
|
Transaction txn = db.startTransaction(true);
|
||||||
try {
|
try {
|
||||||
// get messages and their status
|
// get messages and their status
|
||||||
GroupId g =
|
GroupId g = introductionGroupFactory
|
||||||
getIntroductionGroup(db.getContact(txn, contactId)).getId();
|
.createIntroductionGroup(db.getContact(txn, contactId))
|
||||||
|
.getId();
|
||||||
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
|
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||||
statuses = db.getMessageStatus(txn, contactId, g);
|
statuses = db.getMessageStatus(txn, contactId, g);
|
||||||
|
|
||||||
@@ -444,17 +430,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private BdfDictionary getSessionState(Transaction txn, GroupId groupId,
|
||||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
|
||||||
try {
|
|
||||||
BdfDictionary meta = BdfDictionary.of(new BdfEntry(READ, read));
|
|
||||||
clientHelper.mergeMessageMetadata(m, meta);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BdfDictionary getSessionState(Transaction txn, GroupId groupId,
|
|
||||||
byte[] sessionId) throws DbException, FormatException {
|
byte[] sessionId) throws DbException, FormatException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -473,7 +449,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
// to find state for introducee
|
// to find state for introducee
|
||||||
Map<MessageId, BdfDictionary> map = clientHelper
|
Map<MessageId, BdfDictionary> map = clientHelper
|
||||||
.getMessageMetadataAsDictionary(txn,
|
.getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
introductionGroupFactory.createLocalGroup().getId());
|
||||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||||
if (Arrays.equals(m.getValue().getRaw(SESSION_ID), sessionId)) {
|
if (Arrays.equals(m.getValue().getRaw(SESSION_ID), sessionId)) {
|
||||||
BdfDictionary state = m.getValue();
|
BdfDictionary state = m.getValue();
|
||||||
@@ -490,35 +466,11 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Group getIntroductionGroup(Contact c) {
|
|
||||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Group getLocalGroup() {
|
|
||||||
return localGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMessage(Transaction txn, BdfDictionary message)
|
|
||||||
throws DbException, FormatException {
|
|
||||||
|
|
||||||
BdfList bdfList = MessageEncoder.encodeMessage(message);
|
|
||||||
byte[] body = clientHelper.toByteArray(bdfList);
|
|
||||||
GroupId groupId = new GroupId(message.getRaw(GROUP_ID));
|
|
||||||
Group group = db.getGroup(txn, groupId);
|
|
||||||
long timestamp =
|
|
||||||
message.getLong(MESSAGE_TIME, System.currentTimeMillis());
|
|
||||||
message.put(MESSAGE_TIME, timestamp);
|
|
||||||
|
|
||||||
Metadata metadata = metadataEncoder.encode(message);
|
|
||||||
|
|
||||||
messageQueueManager
|
|
||||||
.sendMessage(txn, group, timestamp, body, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteMessage(Transaction txn, MessageId messageId)
|
private void deleteMessage(Transaction txn, MessageId messageId)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|
||||||
db.deleteMessage(txn, messageId);
|
db.deleteMessage(txn, messageId);
|
||||||
db.deleteMessageMetadata(txn, messageId);
|
db.deleteMessageMetadata(txn, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ package org.briarproject.introduction;
|
|||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.MessageQueueManager;
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
import org.briarproject.api.introduction.IntroductionManager;
|
import org.briarproject.api.introduction.IntroductionManager;
|
||||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.api.system.Clock;
|
import org.briarproject.api.system.Clock;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
package org.briarproject.introduction;
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.Metadata;
|
||||||
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
@@ -18,9 +32,43 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
|||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
|
|
||||||
public class MessageEncoder {
|
class MessageSender {
|
||||||
|
|
||||||
public static BdfList encodeMessage(BdfDictionary d)
|
final private DatabaseComponent db;
|
||||||
|
final private ClientHelper clientHelper;
|
||||||
|
final private Clock clock;
|
||||||
|
final private MetadataEncoder metadataEncoder;
|
||||||
|
final private MessageQueueManager messageQueueManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MessageSender(DatabaseComponent db, ClientHelper clientHelper, Clock clock,
|
||||||
|
MetadataEncoder metadataEncoder,
|
||||||
|
MessageQueueManager messageQueueManager) {
|
||||||
|
|
||||||
|
this.db = db;
|
||||||
|
this.clientHelper = clientHelper;
|
||||||
|
this.clock = clock;
|
||||||
|
this.metadataEncoder = metadataEncoder;
|
||||||
|
this.messageQueueManager = messageQueueManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(Transaction txn, BdfDictionary message)
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
BdfList bdfList = encodeMessage(message);
|
||||||
|
byte[] body = clientHelper.toByteArray(bdfList);
|
||||||
|
GroupId groupId = new GroupId(message.getRaw(GROUP_ID));
|
||||||
|
Group group = db.getGroup(txn, groupId);
|
||||||
|
long timestamp = clock.currentTimeMillis();
|
||||||
|
|
||||||
|
message.put(MESSAGE_TIME, timestamp);
|
||||||
|
Metadata metadata = metadataEncoder.encode(message);
|
||||||
|
|
||||||
|
messageQueueManager
|
||||||
|
.sendMessage(txn, group, timestamp, body, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfList encodeMessage(BdfDictionary d)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
|
|
||||||
BdfList body;
|
BdfList body;
|
||||||
@@ -39,7 +87,7 @@ public class MessageEncoder {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BdfList encodeRequest(BdfDictionary d)
|
private BdfList encodeRequest(BdfDictionary d)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
BdfList list = BdfList.of(TYPE_REQUEST, d.getRaw(SESSION_ID),
|
BdfList list = BdfList.of(TYPE_REQUEST, d.getRaw(SESSION_ID),
|
||||||
d.getString(NAME), d.getRaw(PUBLIC_KEY));
|
d.getString(NAME), d.getRaw(PUBLIC_KEY));
|
||||||
@@ -50,7 +98,7 @@ public class MessageEncoder {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BdfList encodeResponse(BdfDictionary d)
|
private BdfList encodeResponse(BdfDictionary d)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
BdfList list = BdfList.of(TYPE_RESPONSE, d.getRaw(SESSION_ID),
|
BdfList list = BdfList.of(TYPE_RESPONSE, d.getRaw(SESSION_ID),
|
||||||
d.getBoolean(ACCEPT));
|
d.getBoolean(ACCEPT));
|
||||||
@@ -64,11 +112,11 @@ public class MessageEncoder {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BdfList encodeAck(BdfDictionary d) throws FormatException {
|
private BdfList encodeAck(BdfDictionary d) throws FormatException {
|
||||||
return BdfList.of(TYPE_ACK, d.getRaw(SESSION_ID));
|
return BdfList.of(TYPE_ACK, d.getRaw(SESSION_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BdfList encodeAbort(BdfDictionary d) throws FormatException {
|
private BdfList encodeAbort(BdfDictionary d) throws FormatException {
|
||||||
return BdfList.of(TYPE_ABORT, d.getRaw(SESSION_ID));
|
return BdfList.of(TYPE_ABORT, d.getRaw(SESSION_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.Bytes;
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
|
import org.briarproject.api.contact.ContactId;
|
||||||
|
import org.briarproject.api.contact.ContactManager;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
|
import org.briarproject.api.identity.AuthorId;
|
||||||
|
import org.briarproject.api.introduction.IntroduceeProtocolState;
|
||||||
|
import org.briarproject.api.introduction.SessionId;
|
||||||
|
import org.briarproject.api.properties.TransportPropertyManager;
|
||||||
|
import org.briarproject.api.sync.ClientId;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||||
|
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.PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||||
|
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.STATE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
|
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class IntroduceeManagerTest extends BriarTestCase {
|
||||||
|
|
||||||
|
final Mockery context;
|
||||||
|
final IntroduceeManager introduceeManager;
|
||||||
|
final DatabaseComponent db;
|
||||||
|
final CryptoComponent cryptoComponent;
|
||||||
|
final ClientHelper clientHelper;
|
||||||
|
final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
final MessageSender messageSender;
|
||||||
|
final TransportPropertyManager transportPropertyManager;
|
||||||
|
final AuthorFactory authorFactory;
|
||||||
|
final ContactManager contactManager;
|
||||||
|
final Clock clock;
|
||||||
|
final Contact introducer;
|
||||||
|
final Contact introducee1;
|
||||||
|
final Contact introducee2;
|
||||||
|
final Group localGroup1;
|
||||||
|
final Group introductionGroup1;
|
||||||
|
final Group introductionGroup2;
|
||||||
|
final Transaction txn;
|
||||||
|
final long time = 42L;
|
||||||
|
final Message localStateMessage;
|
||||||
|
final ClientId clientId;
|
||||||
|
final SessionId sessionId;
|
||||||
|
final Message message1;
|
||||||
|
|
||||||
|
public IntroduceeManagerTest() {
|
||||||
|
context = new Mockery();
|
||||||
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
|
messageSender = context.mock(MessageSender.class);
|
||||||
|
db = context.mock(DatabaseComponent.class);
|
||||||
|
cryptoComponent = context.mock(CryptoComponent.class);
|
||||||
|
clientHelper = context.mock(ClientHelper.class);
|
||||||
|
clock = context.mock(Clock.class);
|
||||||
|
introductionGroupFactory =
|
||||||
|
context.mock(IntroductionGroupFactory.class);
|
||||||
|
transportPropertyManager = context.mock(TransportPropertyManager.class);
|
||||||
|
authorFactory = context.mock(AuthorFactory.class);
|
||||||
|
contactManager = context.mock(ContactManager.class);
|
||||||
|
|
||||||
|
introduceeManager = new IntroduceeManager(messageSender, db,
|
||||||
|
clientHelper, clock, cryptoComponent, transportPropertyManager,
|
||||||
|
authorFactory, contactManager, introductionGroupFactory);
|
||||||
|
|
||||||
|
AuthorId authorId0 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author0 = new Author(authorId0, "Introducer",
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
AuthorId localAuthorId = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId0 = new ContactId(234);
|
||||||
|
introducer = new Contact(contactId0, author0, localAuthorId, true);
|
||||||
|
|
||||||
|
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author1 = new Author(authorId1, "Introducee1",
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId1 = new ContactId(234);
|
||||||
|
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
|
||||||
|
|
||||||
|
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author2 = new Author(authorId2, "Introducee2",
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
ContactId contactId2 = new ContactId(235);
|
||||||
|
introducee2 = new Contact(contactId2, author2, localAuthorId, true);
|
||||||
|
|
||||||
|
clientId = IntroductionManagerImpl.CLIENT_ID;
|
||||||
|
localGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
|
||||||
|
sessionId = new SessionId(TestUtils.getRandomId());
|
||||||
|
localStateMessage = new Message(
|
||||||
|
new MessageId(TestUtils.getRandomId()),
|
||||||
|
localGroup1.getId(),
|
||||||
|
time,
|
||||||
|
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
|
||||||
|
);
|
||||||
|
message1 = new Message(
|
||||||
|
new MessageId(TestUtils.getRandomId()),
|
||||||
|
introductionGroup1.getId(),
|
||||||
|
time,
|
||||||
|
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncomingRequestMessage()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_REQUEST);
|
||||||
|
msg.put(GROUP_ID, introductionGroup1.getId());
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(MESSAGE_ID, message1.getId());
|
||||||
|
msg.put(MESSAGE_TIME, time);
|
||||||
|
msg.put(NAME, introducee2.getAuthor().getName());
|
||||||
|
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||||
|
|
||||||
|
final BdfDictionary state =
|
||||||
|
initializeSessionState(txn, introductionGroup1.getId(), msg);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).mergeMessageMetadata(txn,
|
||||||
|
localStateMessage.getId(), state);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introduceeManager.incomingMessage(txn, state, msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncomingResponseMessage()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_RESPONSE);
|
||||||
|
msg.put(GROUP_ID, introductionGroup1.getId());
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(MESSAGE_ID, message1.getId());
|
||||||
|
msg.put(MESSAGE_TIME, time);
|
||||||
|
msg.put(NAME, introducee2.getAuthor().getName());
|
||||||
|
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||||
|
|
||||||
|
final BdfDictionary state =
|
||||||
|
initializeSessionState(txn, introductionGroup1.getId(), msg);
|
||||||
|
state.put(STATE, IntroduceeProtocolState.AWAIT_RESPONSES.ordinal());
|
||||||
|
|
||||||
|
// turn request message into a response
|
||||||
|
msg.put(ACCEPT, true);
|
||||||
|
msg.put(TIME, time);
|
||||||
|
msg.put(E_PUBLIC_KEY, TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
msg.put(TRANSPORT, new BdfDictionary());
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).mergeMessageMetadata(txn,
|
||||||
|
localStateMessage.getId(), state);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introduceeManager.incomingMessage(txn, state, msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfDictionary initializeSessionState(final Transaction txn,
|
||||||
|
final GroupId groupId, final BdfDictionary msg)
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final SecureRandom secureRandom = context.mock(SecureRandom.class);
|
||||||
|
final Bytes salt = new Bytes(new byte[64]);
|
||||||
|
final BdfDictionary groupMetadata = BdfDictionary.of(
|
||||||
|
new BdfEntry(CONTACT, introducee1.getId().getInt())
|
||||||
|
);
|
||||||
|
final boolean contactExists = true;
|
||||||
|
final BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(STORAGE_ID, localStateMessage.getId());
|
||||||
|
state.put(STATE, AWAIT_REQUEST.getValue());
|
||||||
|
state.put(ROLE, ROLE_INTRODUCEE);
|
||||||
|
state.put(GROUP_ID, groupId);
|
||||||
|
state.put(INTRODUCER, introducer.getAuthor().getName());
|
||||||
|
state.put(CONTACT_ID_1, introducer.getId().getInt());
|
||||||
|
state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
||||||
|
state.put(NOT_OUR_RESPONSE, localStateMessage.getId());
|
||||||
|
state.put(ANSWERED, false);
|
||||||
|
state.put(EXISTS, true);
|
||||||
|
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(time));
|
||||||
|
oneOf(cryptoComponent).getSecureRandom();
|
||||||
|
will(returnValue(secureRandom));
|
||||||
|
oneOf(secureRandom).nextBytes(salt.getBytes());
|
||||||
|
oneOf(introductionGroupFactory).createLocalGroup();
|
||||||
|
will(returnValue(localGroup1));
|
||||||
|
oneOf(clientHelper)
|
||||||
|
.createMessage(localGroup1.getId(), time, BdfList.of(salt));
|
||||||
|
will(returnValue(localStateMessage));
|
||||||
|
|
||||||
|
// who is making the introduction? who is the introducer?
|
||||||
|
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
||||||
|
groupId);
|
||||||
|
will(returnValue(groupMetadata));
|
||||||
|
oneOf(db).getContact(txn, introducer.getId());
|
||||||
|
will(returnValue(introducer));
|
||||||
|
|
||||||
|
// create remote author to check if contact exists
|
||||||
|
oneOf(authorFactory).createAuthor(introducee2.getAuthor().getName(),
|
||||||
|
introducee2.getAuthor().getPublicKey());
|
||||||
|
will(returnValue(introducee2.getAuthor()));
|
||||||
|
oneOf(contactManager)
|
||||||
|
.contactExists(txn, introducee2.getAuthor().getId(),
|
||||||
|
introducer.getLocalAuthorId());
|
||||||
|
will(returnValue(contactExists));
|
||||||
|
|
||||||
|
// store session state
|
||||||
|
oneOf(clientHelper)
|
||||||
|
.addLocalMessage(txn, localStateMessage, clientId, state,
|
||||||
|
false);
|
||||||
|
}});
|
||||||
|
|
||||||
|
BdfDictionary result = introduceeManager.initialize(txn, groupId, msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.Bytes;
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
|
import org.briarproject.api.contact.ContactId;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
|
import org.briarproject.api.identity.AuthorId;
|
||||||
|
import org.briarproject.api.sync.ClientId;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.introduction.IntroducerProtocolState.AWAIT_RESPONSES;
|
||||||
|
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class IntroducerManagerTest extends BriarTestCase {
|
||||||
|
|
||||||
|
final Mockery context;
|
||||||
|
final IntroducerManager introducerManager;
|
||||||
|
final CryptoComponent cryptoComponent;
|
||||||
|
final ClientHelper clientHelper;
|
||||||
|
final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
final MessageSender messageSender;
|
||||||
|
final Clock clock;
|
||||||
|
final Contact introducee1;
|
||||||
|
final Contact introducee2;
|
||||||
|
final Group localGroup0;
|
||||||
|
final Group introductionGroup1;
|
||||||
|
final Group introductionGroup2;
|
||||||
|
|
||||||
|
public IntroducerManagerTest() {
|
||||||
|
context = new Mockery();
|
||||||
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
|
messageSender = context.mock(MessageSender.class);
|
||||||
|
cryptoComponent = context.mock(CryptoComponent.class);
|
||||||
|
clientHelper = context.mock(ClientHelper.class);
|
||||||
|
clock = context.mock(Clock.class);
|
||||||
|
introductionGroupFactory =
|
||||||
|
context.mock(IntroductionGroupFactory.class);
|
||||||
|
|
||||||
|
introducerManager =
|
||||||
|
new IntroducerManager(messageSender, clientHelper, clock,
|
||||||
|
cryptoComponent, introductionGroupFactory);
|
||||||
|
|
||||||
|
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author1 = new Author(authorId1, "Introducee1",
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId1 = new ContactId(234);
|
||||||
|
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
|
||||||
|
|
||||||
|
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author2 = new Author(authorId2, "Introducee2",
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId2 = new ContactId(235);
|
||||||
|
introducee2 = new Contact(contactId2, author2, localAuthorId2, true);
|
||||||
|
|
||||||
|
localGroup0 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
getClientId(), new byte[0]);
|
||||||
|
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
getClientId(), new byte[0]);
|
||||||
|
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
getClientId(), new byte[0]);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMakeIntroduction() throws DbException, FormatException {
|
||||||
|
final Transaction txn = new Transaction(null, false);
|
||||||
|
final long time = 42L;
|
||||||
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
|
final SecureRandom secureRandom = context.mock(SecureRandom.class);
|
||||||
|
final Bytes salt = new Bytes(new byte[64]);
|
||||||
|
final Message msg = new Message(new MessageId(TestUtils.getRandomId()),
|
||||||
|
localGroup0.getId(), time, TestUtils.getRandomBytes(64));
|
||||||
|
final BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(SESSION_ID, msg.getId());
|
||||||
|
state.put(STORAGE_ID, msg.getId());
|
||||||
|
state.put(STATE, PREPARE_REQUESTS.getValue());
|
||||||
|
state.put(ROLE, ROLE_INTRODUCER);
|
||||||
|
state.put(GROUP_ID_1, introductionGroup1.getId());
|
||||||
|
state.put(GROUP_ID_2, introductionGroup2.getId());
|
||||||
|
state.put(CONTACT_1, introducee1.getAuthor().getName());
|
||||||
|
state.put(CONTACT_2, introducee2.getAuthor().getName());
|
||||||
|
state.put(CONTACT_ID_1, introducee1.getId().getInt());
|
||||||
|
state.put(CONTACT_ID_2, introducee2.getId().getInt());
|
||||||
|
state.put(AUTHOR_ID_1, introducee1.getAuthor().getId());
|
||||||
|
state.put(AUTHOR_ID_2, introducee2.getAuthor().getId());
|
||||||
|
final BdfDictionary state2 = (BdfDictionary) state.clone();
|
||||||
|
state2.put(STATE, AWAIT_RESPONSES.getValue());
|
||||||
|
|
||||||
|
final BdfDictionary msg1 = new BdfDictionary();
|
||||||
|
msg1.put(TYPE, TYPE_REQUEST);
|
||||||
|
msg1.put(SESSION_ID, state.getRaw(SESSION_ID));
|
||||||
|
msg1.put(GROUP_ID, state.getRaw(GROUP_ID_1));
|
||||||
|
msg1.put(NAME, state.getString(CONTACT_2));
|
||||||
|
msg1.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||||
|
final BdfDictionary msg1send = (BdfDictionary) msg1.clone();
|
||||||
|
msg1send.put(MESSAGE_TIME, time);
|
||||||
|
|
||||||
|
final BdfDictionary msg2 = new BdfDictionary();
|
||||||
|
msg2.put(TYPE, TYPE_REQUEST);
|
||||||
|
msg2.put(SESSION_ID, state.getRaw(SESSION_ID));
|
||||||
|
msg2.put(GROUP_ID, state.getRaw(GROUP_ID_2));
|
||||||
|
msg2.put(NAME, state.getString(CONTACT_1));
|
||||||
|
msg2.put(PUBLIC_KEY, introducee1.getAuthor().getPublicKey());
|
||||||
|
final BdfDictionary msg2send = (BdfDictionary) msg2.clone();
|
||||||
|
msg2send.put(MESSAGE_TIME, time);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// initialize and store session state
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(time));
|
||||||
|
oneOf(cryptoComponent).getSecureRandom();
|
||||||
|
will(returnValue(secureRandom));
|
||||||
|
oneOf(secureRandom).nextBytes(salt.getBytes());
|
||||||
|
oneOf(introductionGroupFactory).createLocalGroup();
|
||||||
|
will(returnValue(localGroup0));
|
||||||
|
oneOf(clientHelper).createMessage(localGroup0.getId(), time,
|
||||||
|
BdfList.of(salt));
|
||||||
|
will(returnValue(msg));
|
||||||
|
oneOf(introductionGroupFactory)
|
||||||
|
.createIntroductionGroup(introducee1);
|
||||||
|
will(returnValue(introductionGroup1));
|
||||||
|
oneOf(introductionGroupFactory)
|
||||||
|
.createIntroductionGroup(introducee2);
|
||||||
|
will(returnValue(introductionGroup2));
|
||||||
|
oneOf(clientHelper).addLocalMessage(txn, msg, getClientId(), state,
|
||||||
|
false);
|
||||||
|
|
||||||
|
// send message
|
||||||
|
oneOf(clientHelper).mergeMessageMetadata(txn, msg.getId(), state2);
|
||||||
|
oneOf(messageSender).sendMessage(txn, msg1send);
|
||||||
|
oneOf(messageSender).sendMessage(txn, msg2send);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introducerManager
|
||||||
|
.makeIntroduction(txn, introducee1, introducee2, null, time);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientId getClientId() {
|
||||||
|
return IntroductionManagerImpl.CLIENT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
|
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
|
import org.briarproject.api.contact.ContactId;
|
||||||
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.api.data.MetadataParser;
|
||||||
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
|
import org.briarproject.api.identity.AuthorId;
|
||||||
|
import org.briarproject.api.introduction.SessionId;
|
||||||
|
import org.briarproject.api.sync.ClientId;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.api.sync.MessageStatus;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
|
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class IntroductionManagerImplTest extends BriarTestCase {
|
||||||
|
|
||||||
|
final Mockery context;
|
||||||
|
final IntroductionManagerImpl introductionManager;
|
||||||
|
final IntroducerManager introducerManager;
|
||||||
|
final IntroduceeManager introduceeManager;
|
||||||
|
final DatabaseComponent db;
|
||||||
|
final PrivateGroupFactory privateGroupFactory;
|
||||||
|
final ClientHelper clientHelper;
|
||||||
|
final MetadataEncoder metadataEncoder;
|
||||||
|
final MessageQueueManager messageQueueManager;
|
||||||
|
final IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
final Clock clock;
|
||||||
|
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
|
||||||
|
final long time = 42L;
|
||||||
|
final Contact introducee1;
|
||||||
|
final Contact introducee2;
|
||||||
|
final Group localGroup0;
|
||||||
|
final Group introductionGroup1;
|
||||||
|
final Group introductionGroup2;
|
||||||
|
final Message message1;
|
||||||
|
Transaction txn;
|
||||||
|
|
||||||
|
public IntroductionManagerImplTest() {
|
||||||
|
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author1 = new Author(authorId1, "Introducee1",
|
||||||
|
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||||
|
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId1 = new ContactId(234);
|
||||||
|
introducee1 = new Contact(contactId1, author1, localAuthorId1, true);
|
||||||
|
|
||||||
|
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
Author author2 = new Author(authorId2, "Introducee2",
|
||||||
|
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||||
|
AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId());
|
||||||
|
ContactId contactId2 = new ContactId(235);
|
||||||
|
introducee2 = new Contact(contactId2, author2, localAuthorId2, true);
|
||||||
|
|
||||||
|
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||||
|
localGroup0 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
clientId, new byte[0]);
|
||||||
|
|
||||||
|
message1 = new Message(
|
||||||
|
new MessageId(TestUtils.getRandomId()),
|
||||||
|
introductionGroup1.getId(),
|
||||||
|
time,
|
||||||
|
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// mock ALL THE THINGS!!!
|
||||||
|
context = new Mockery();
|
||||||
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
|
introducerManager = context.mock(IntroducerManager.class);
|
||||||
|
introduceeManager = context.mock(IntroduceeManager.class);
|
||||||
|
db = context.mock(DatabaseComponent.class);
|
||||||
|
privateGroupFactory = context.mock(PrivateGroupFactory.class);
|
||||||
|
clientHelper = context.mock(ClientHelper.class);
|
||||||
|
metadataEncoder =
|
||||||
|
context.mock(MetadataEncoder.class);
|
||||||
|
messageQueueManager =
|
||||||
|
context.mock(MessageQueueManager.class);
|
||||||
|
MetadataParser metadataParser = context.mock(MetadataParser.class);
|
||||||
|
introductionGroupFactory = context.mock(IntroductionGroupFactory.class);
|
||||||
|
clock = context.mock(Clock.class);
|
||||||
|
|
||||||
|
introductionManager = new IntroductionManagerImpl(
|
||||||
|
db, clientHelper, metadataParser, clock, introducerManager,
|
||||||
|
introduceeManager, introductionGroupFactory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMakeIntroduction() throws DbException, FormatException {
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).startTransaction(false);
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(introducerManager)
|
||||||
|
.makeIntroduction(txn, introducee1, introducee2, null,
|
||||||
|
time);
|
||||||
|
oneOf(db).endTransaction(txn);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager
|
||||||
|
.makeIntroduction(introducee1, introducee2, null, time);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertTrue(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptIntroduction() throws DbException, FormatException {
|
||||||
|
final BdfDictionary state = BdfDictionary.of(
|
||||||
|
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
|
||||||
|
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
|
||||||
|
);
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).startTransaction(false);
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(db).getContact(txn, introducee1.getId());
|
||||||
|
will(returnValue(introducee1));
|
||||||
|
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
|
||||||
|
will(returnValue(introductionGroup1));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||||
|
will(returnValue(state));
|
||||||
|
oneOf(introduceeManager).acceptIntroduction(txn, state, time);
|
||||||
|
oneOf(db).endTransaction(txn);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager
|
||||||
|
.acceptIntroduction(introducee1.getId(), sessionId, time);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertTrue(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeclineIntroduction() throws DbException, FormatException {
|
||||||
|
final BdfDictionary state = BdfDictionary.of(
|
||||||
|
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
|
||||||
|
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
|
||||||
|
);
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).startTransaction(false);
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(db).getContact(txn, introducee1.getId());
|
||||||
|
will(returnValue(introducee1));
|
||||||
|
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
|
||||||
|
will(returnValue(introductionGroup1));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||||
|
will(returnValue(state));
|
||||||
|
oneOf(introduceeManager).declineIntroduction(txn, state, time);
|
||||||
|
oneOf(db).endTransaction(txn);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager
|
||||||
|
.declineIntroduction(introducee1.getId(), sessionId, time);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertTrue(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIntroductionMessages()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final Map<MessageId, BdfDictionary> metadata = Collections.emptyMap();
|
||||||
|
final Collection<MessageStatus> statuses = Collections.emptyList();
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).startTransaction(true);
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(db).getContact(txn, introducee1.getId());
|
||||||
|
will(returnValue(introducee1));
|
||||||
|
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
|
||||||
|
will(returnValue(introductionGroup1));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
introductionGroup1.getId());
|
||||||
|
will(returnValue(metadata));
|
||||||
|
oneOf(db).getMessageStatus(txn, introducee1.getId(),
|
||||||
|
introductionGroup1.getId());
|
||||||
|
will(returnValue(statuses));
|
||||||
|
oneOf(db).endTransaction(txn);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager.getIntroductionMessages(introducee1.getId());
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertTrue(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncomingRequestMessage()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_REQUEST);
|
||||||
|
|
||||||
|
final BdfDictionary state = new BdfDictionary();
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(introduceeManager)
|
||||||
|
.initialize(txn, introductionGroup1.getId(), msg);
|
||||||
|
will(returnValue(state));
|
||||||
|
oneOf(introduceeManager)
|
||||||
|
.incomingMessage(txn, state, msg);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager
|
||||||
|
.incomingMessage(txn, message1, new BdfList(), msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncomingResponseMessage()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
final BdfDictionary msg = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_RESPONSE),
|
||||||
|
new BdfEntry(SESSION_ID, sessionId)
|
||||||
|
);
|
||||||
|
|
||||||
|
final BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(ROLE, ROLE_INTRODUCER);
|
||||||
|
state.put(GROUP_ID_1, introductionGroup1.getId());
|
||||||
|
state.put(GROUP_ID_2, introductionGroup2.getId());
|
||||||
|
|
||||||
|
txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||||
|
will(returnValue(state));
|
||||||
|
oneOf(introducerManager).incomingMessage(txn, state, msg);
|
||||||
|
}});
|
||||||
|
|
||||||
|
introductionManager
|
||||||
|
.incomingMessage(txn, message1, new BdfList(), msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,357 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.TransportId;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.api.introduction.SessionId;
|
||||||
|
import org.briarproject.api.sync.ClientId;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
import org.briarproject.system.SystemClock;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
|
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||||
|
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class IntroductionValidatorTest extends BriarTestCase {
|
||||||
|
|
||||||
|
private final Mockery context = new Mockery();
|
||||||
|
private final Group group;
|
||||||
|
private final Message message;
|
||||||
|
private final IntroductionValidator validator;
|
||||||
|
private final Clock clock = new SystemClock();
|
||||||
|
|
||||||
|
public IntroductionValidatorTest() {
|
||||||
|
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||||
|
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||||
|
byte[] descriptor = TestUtils.getRandomBytes(12);
|
||||||
|
group = new Group(groupId, clientId, descriptor);
|
||||||
|
|
||||||
|
MessageId messageId = new MessageId(TestUtils.getRandomId());
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
byte[] raw = TestUtils.getRandomBytes(123);
|
||||||
|
message = new Message(messageId, group.getId(), timestamp, raw);
|
||||||
|
|
||||||
|
|
||||||
|
ClientHelper clientHelper = context.mock(ClientHelper.class);
|
||||||
|
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
|
||||||
|
validator = new IntroductionValidator(clientHelper, metadataEncoder,
|
||||||
|
clock);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction Requests
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateProperIntroductionRequest() throws IOException {
|
||||||
|
final byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
final String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
|
final byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
final String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(TYPE_REQUEST, sessionId,
|
||||||
|
name, publicKey, text);
|
||||||
|
|
||||||
|
final BdfDictionary result =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertEquals(Long.valueOf(TYPE_REQUEST), result.getLong(TYPE));
|
||||||
|
assertEquals(sessionId, result.getRaw(SESSION_ID));
|
||||||
|
assertEquals(name, result.getString(NAME));
|
||||||
|
assertEquals(publicKey, result.getRaw(PUBLIC_KEY));
|
||||||
|
assertEquals(text, result.getString(MSG));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionRequestWithNoName() throws IOException {
|
||||||
|
BdfDictionary msg = getValidIntroductionRequest();
|
||||||
|
|
||||||
|
// no NAME is message
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getRaw(PUBLIC_KEY));
|
||||||
|
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionRequestWithLongName() throws IOException {
|
||||||
|
// too long NAME in message
|
||||||
|
BdfDictionary msg = getValidIntroductionRequest();
|
||||||
|
msg.put(NAME, msg.get(NAME) + "x");
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
|
||||||
|
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionRequestWithWrongType()
|
||||||
|
throws IOException {
|
||||||
|
// wrong message type
|
||||||
|
BdfDictionary msg = getValidIntroductionRequest();
|
||||||
|
msg.put(TYPE, 324234);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
|
||||||
|
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfDictionary getValidIntroductionRequest() throws FormatException {
|
||||||
|
byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
|
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH);
|
||||||
|
|
||||||
|
BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_REQUEST);
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(NAME, name);
|
||||||
|
msg.put(PUBLIC_KEY, publicKey);
|
||||||
|
msg.put(MSG, text);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction Responses
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateIntroductionAcceptResponse() throws IOException {
|
||||||
|
byte[] groupId = TestUtils.getRandomId();
|
||||||
|
byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
long time = clock.currentTimeMillis();
|
||||||
|
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
String transportId = TestUtils
|
||||||
|
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
|
||||||
|
BdfDictionary tProps = BdfDictionary.of(
|
||||||
|
new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH),
|
||||||
|
TestUtils.getRandomString(MAX_PROPERTY_LENGTH))
|
||||||
|
);
|
||||||
|
BdfDictionary tp = BdfDictionary.of(
|
||||||
|
new BdfEntry(transportId, tProps)
|
||||||
|
);
|
||||||
|
|
||||||
|
BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_RESPONSE);
|
||||||
|
msg.put(GROUP_ID, groupId);
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(ACCEPT, true);
|
||||||
|
msg.put(TIME, time);
|
||||||
|
msg.put(E_PUBLIC_KEY, publicKey);
|
||||||
|
msg.put(TRANSPORT, tp);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(TYPE_RESPONSE, msg.getRaw(SESSION_ID),
|
||||||
|
msg.getBoolean(ACCEPT), msg.getLong(TIME),
|
||||||
|
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
|
||||||
|
|
||||||
|
final BdfDictionary result =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertEquals(Long.valueOf(TYPE_RESPONSE), result.getLong(TYPE));
|
||||||
|
assertEquals(sessionId, result.getRaw(SESSION_ID));
|
||||||
|
assertEquals(true, result.getBoolean(ACCEPT));
|
||||||
|
assertEquals(publicKey, result.getRaw(E_PUBLIC_KEY));
|
||||||
|
assertEquals(tp, result.getDictionary(TRANSPORT));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateIntroductionDeclineResponse()
|
||||||
|
throws IOException {
|
||||||
|
BdfDictionary msg = getValidIntroductionResponse(false);
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getBoolean(ACCEPT));
|
||||||
|
|
||||||
|
BdfDictionary result = validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertFalse(result.getBoolean(ACCEPT));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionResponseWithoutAccept()
|
||||||
|
throws IOException {
|
||||||
|
BdfDictionary msg = getValidIntroductionResponse(false);
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionResponseWithBrokenTp()
|
||||||
|
throws IOException {
|
||||||
|
BdfDictionary msg = getValidIntroductionResponse(true);
|
||||||
|
BdfDictionary tp = msg.getDictionary(TRANSPORT);
|
||||||
|
tp.put(TestUtils
|
||||||
|
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH), "X");
|
||||||
|
msg.put(TRANSPORT, tp);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getBoolean(ACCEPT), msg.getLong(TIME),
|
||||||
|
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionResponseWithoutPublicKey()
|
||||||
|
throws IOException {
|
||||||
|
BdfDictionary msg = getValidIntroductionResponse(true);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getBoolean(ACCEPT), msg.getLong(TIME),
|
||||||
|
msg.getDictionary(TRANSPORT));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfDictionary getValidIntroductionResponse(boolean accept)
|
||||||
|
throws FormatException {
|
||||||
|
|
||||||
|
byte[] groupId = TestUtils.getRandomId();
|
||||||
|
byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
long time = clock.currentTimeMillis();
|
||||||
|
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
String transportId = TestUtils
|
||||||
|
.getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
|
||||||
|
BdfDictionary tProps = BdfDictionary.of(
|
||||||
|
new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH),
|
||||||
|
TestUtils.getRandomString(MAX_PROPERTY_LENGTH))
|
||||||
|
);
|
||||||
|
BdfDictionary tp = BdfDictionary.of(
|
||||||
|
new BdfEntry(transportId, tProps)
|
||||||
|
);
|
||||||
|
|
||||||
|
BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_RESPONSE);
|
||||||
|
msg.put(GROUP_ID, groupId);
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(ACCEPT, accept);
|
||||||
|
if (accept) {
|
||||||
|
msg.put(TIME, time);
|
||||||
|
msg.put(E_PUBLIC_KEY, publicKey);
|
||||||
|
msg.put(TRANSPORT, tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction ACK
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateProperIntroductionAck() throws IOException {
|
||||||
|
final byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
|
||||||
|
BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_ACK);
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
|
||||||
|
|
||||||
|
BdfDictionary result =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertEquals(Long.valueOf(TYPE_ACK), result.getLong(TYPE));
|
||||||
|
assertEquals(sessionId, result.getRaw(SESSION_ID));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateTooLongIntroductionAck() throws IOException {
|
||||||
|
BdfDictionary msg = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_ACK),
|
||||||
|
new BdfEntry(SESSION_ID, TestUtils.getRandomId()),
|
||||||
|
new BdfEntry("garbage", TestUtils.getRandomString(255))
|
||||||
|
);
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getString("garbage"));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateIntroductionAckWithLongSessionId() throws IOException {
|
||||||
|
BdfDictionary msg = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_ACK),
|
||||||
|
new BdfEntry(SESSION_ID, new byte[SessionId.LENGTH + 1])
|
||||||
|
);
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction Abort
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateProperIntroductionAbort() throws IOException {
|
||||||
|
byte[] sessionId = TestUtils.getRandomId();
|
||||||
|
|
||||||
|
BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_ABORT);
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
|
||||||
|
|
||||||
|
BdfDictionary result =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertEquals(Long.valueOf(TYPE_ABORT), result.getLong(TYPE));
|
||||||
|
assertEquals(sessionId, result.getRaw(SESSION_ID));
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateTooLongIntroductionAbort() throws IOException {
|
||||||
|
BdfDictionary msg = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_ABORT),
|
||||||
|
new BdfEntry(SESSION_ID, TestUtils.getRandomId()),
|
||||||
|
new BdfEntry("garbage", TestUtils.getRandomString(255))
|
||||||
|
);
|
||||||
|
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
|
||||||
|
msg.getString("garbage"));
|
||||||
|
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
|
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||||
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.Metadata;
|
||||||
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.introduction.SessionId;
|
||||||
|
import org.briarproject.api.sync.ClientId;
|
||||||
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.system.Clock;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||||
|
|
||||||
|
public class MessageSenderTest extends BriarTestCase {
|
||||||
|
|
||||||
|
final Mockery context;
|
||||||
|
final MessageSender messageSender;
|
||||||
|
final DatabaseComponent db;
|
||||||
|
final PrivateGroupFactory privateGroupFactory;
|
||||||
|
final ClientHelper clientHelper;
|
||||||
|
final MetadataEncoder metadataEncoder;
|
||||||
|
final MessageQueueManager messageQueueManager;
|
||||||
|
final Clock clock;
|
||||||
|
|
||||||
|
public MessageSenderTest() {
|
||||||
|
context = new Mockery();
|
||||||
|
db = context.mock(DatabaseComponent.class);
|
||||||
|
privateGroupFactory = context.mock(PrivateGroupFactory.class);
|
||||||
|
clientHelper = context.mock(ClientHelper.class);
|
||||||
|
metadataEncoder =
|
||||||
|
context.mock(MetadataEncoder.class);
|
||||||
|
messageQueueManager =
|
||||||
|
context.mock(MessageQueueManager.class);
|
||||||
|
clock = context.mock(Clock.class);
|
||||||
|
|
||||||
|
messageSender =
|
||||||
|
new MessageSender(db, clientHelper, clock, metadataEncoder,
|
||||||
|
messageQueueManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendMessage() throws DbException, FormatException {
|
||||||
|
final Transaction txn = new Transaction(null, false);
|
||||||
|
final Group privateGroup = new Group(new GroupId(TestUtils.getRandomId()),
|
||||||
|
new ClientId(TestUtils.getRandomId()), new byte[0]);
|
||||||
|
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
|
||||||
|
final long time = 42L;
|
||||||
|
final BdfDictionary msg = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_ACK),
|
||||||
|
new BdfEntry(GROUP_ID, privateGroup.getId()),
|
||||||
|
new BdfEntry(SESSION_ID, sessionId)
|
||||||
|
);
|
||||||
|
final BdfList bodyList =
|
||||||
|
BdfList.of(TYPE_ACK, msg.getRaw(SESSION_ID));
|
||||||
|
final byte[] body = TestUtils.getRandomBytes(8);
|
||||||
|
final Metadata metadata = new Metadata();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).toByteArray(bodyList);
|
||||||
|
will(returnValue(body));
|
||||||
|
oneOf(db).getGroup(txn, privateGroup.getId());
|
||||||
|
will(returnValue(privateGroup));
|
||||||
|
oneOf(metadataEncoder).encode(msg);
|
||||||
|
will(returnValue(metadata));
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(time));
|
||||||
|
oneOf(messageQueueManager)
|
||||||
|
.sendMessage(txn, privateGroup, time, body, metadata);
|
||||||
|
}});
|
||||||
|
|
||||||
|
messageSender.sendMessage(txn, msg);
|
||||||
|
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user