Merge branch '709-reveal-relationships' into 'master'

Mark relationship visible when syncing group with peer

This branch updates the private group invitation protocol to use @grote's new method for marking a contact relationship visible to the group.

I've changed the method slightly because the protocol state machine allows us to leave and re-enter the BOTH_JOINED state (see diagram on #659), so the relationship may already be visible when the method is called. In that case the visibility isn't updated, so we stick with whichever of revealed-by-us and revealed-by-contact happened first.

See merge request !402
This commit is contained in:
akwizgran
2016-11-14 10:32:56 +00:00
5 changed files with 79 additions and 10 deletions

View File

@@ -31,6 +31,8 @@
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.privategroup.invitation;
import org.briarproject.api.clients.ProtocolStateException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
@@ -26,22 +27,41 @@ public interface GroupInvitationManager {
/**
* Sends an invitation to share the given private group with the given
* contact, including an optional message.
*
* @throws ProtocolStateException if the group is no longer eligible to be
* shared with the contact, for example because an invitation is already
* pending.
*/
void sendInvitation(GroupId g, ContactId c, @Nullable String message,
long timestamp, byte[] signature) throws DbException;
/**
* Responds to a pending private group invitation from the given contact.
*
* @throws ProtocolStateException if the invitation is no longer pending,
* for example because the group has been dissolved.
*/
void respondToInvitation(ContactId c, PrivateGroup g, boolean accept)
throws DbException;
/**
* Responds to a pending private group invitation from the given contact.
*
* @throws ProtocolStateException if the invitation is no longer pending,
* for example because the group has been dissolved.
*/
void respondToInvitation(ContactId c, SessionId s, boolean accept)
throws DbException;
/**
* Makes the user's relationship with the given contact visible to the
* given private group.
*
* @throws ProtocolStateException if the relationship is no longer eligible
* to be revealed, for example because the contact has revealed it.
*/
void revealRelationship(ContactId c, GroupId g) throws DbException;
/**
* Returns all private group invitation messages related to the given
* contact.

View File

@@ -443,21 +443,26 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList members = meta.getList(GROUP_KEY_MEMBERS);
Visibility v = INVISIBLE;
boolean foundMember = false;
boolean foundMember = false, changed = false;
for (int i = 0 ; i < members.size(); i++) {
BdfDictionary d = members.getDictionary(i);
AuthorId memberId = new AuthorId(d.getRaw(KEY_MEMBER_ID));
if (a.equals(memberId)) {
foundMember = true;
Visibility vOld = getVisibility(d);
if (vOld != INVISIBLE) throw new ProtocolStateException();
v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US;
d.put(GROUP_KEY_VISIBILITY, v.getInt());
// Don't update the visibility if the contact is already visible
if (getVisibility(d) == INVISIBLE) {
changed = true;
v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US;
d.put(GROUP_KEY_VISIBILITY, v.getInt());
}
break;
}
}
if (!foundMember) throw new ProtocolStateException();
clientHelper.mergeGroupMetadata(txn, g, meta);
txn.attach(new ContactRelationshipRevealedEvent(g, v));
if (changed) {
clientHelper.mergeGroupMetadata(txn, g, meta);
txn.attach(new ContactRelationshipRevealedEvent(g, v));
}
}
@Override

View File

@@ -315,6 +315,30 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
}
}
@Override
public void revealRelationship(ContactId c, GroupId g) throws DbException {
Transaction txn = db.startTransaction(false);
try {
// Look up the session
Contact contact = db.getContact(txn, c);
GroupId contactGroupId = getContactGroup(contact).getId();
StoredSession ss = getSession(txn, contactGroupId, getSessionId(g));
if (ss == null) throw new IllegalArgumentException();
// Parse the session
PeerSession session = sessionParser
.parsePeerSession(contactGroupId, ss.bdfSession);
// Handle the join action
session = peerEngine.onJoinAction(txn, session);
// Store the updated session
storeSession(txn, ss.storageId, session);
db.commitTransaction(txn);
} catch (FormatException e) {
throw new DbException(e);
} finally {
db.endTransaction(txn);
}
}
private <S extends Session> S handleAction(Transaction txn,
LocalAction type, S session, ProtocolEngine<S> engine)
throws DbException, FormatException {
@@ -435,9 +459,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
db.commitTransaction(txn);
// If there's no session, the contact can be invited
if (ss == null) return true;
// If there's a session, it should be a creator session
if (sessionParser.getRole(ss.bdfSession) != CREATOR)
throw new IllegalArgumentException();
// If the session's in the start state, the contact can be invited
CreatorSession session = sessionParser
.parseCreatorSession(contactGroupId, ss.bdfSession);

View File

@@ -3,6 +3,8 @@ package org.briarproject.privategroup.invitation;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.ProtocolStateException;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
@@ -179,6 +181,7 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// The relationship is already marked visible to the group
// Move to the BOTH_JOINED state
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
@@ -228,6 +231,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
try {
// Mark the relationship visible to the group, revealed by contact
relationshipRevealed(txn, s, true);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Move to the BOTH_JOINED state
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
@@ -254,6 +263,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
Message sent = sendJoinMessage(txn, s, false);
// Start syncing the private group with the contact
syncPrivateGroupWithContact(txn, s, true);
// Mark the relationship visible to the group, revealed by contact
relationshipRevealed(txn, s, true);
// Move to the BOTH_JOINED state
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), m.getId(), sent.getTimestamp(), BOTH_JOINED);
@@ -266,6 +277,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
return abort(txn, s);
// Start syncing the private group with the contact
syncPrivateGroupWithContact(txn, s, true);
// Mark the relationship visible to the group, revealed by us
relationshipRevealed(txn, s, false);
// Move to the BOTH_JOINED state
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
@@ -321,4 +334,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
ERROR);
}
private void relationshipRevealed(Transaction txn, PeerSession s,
boolean byContact) throws DbException, FormatException {
ContactId contactId = getContactId(txn, s.getContactGroupId());
Contact contact = db.getContact(txn, contactId);
privateGroupManager.relationshipRevealed(txn, s.getPrivateGroupId(),
contact.getAuthor().getId(), byContact);
}
}