mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 05:39:53 +01:00
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:
2
.idea/codeStyleSettings.xml
generated
2
.idea/codeStyleSettings.xml
generated
@@ -31,6 +31,8 @@
|
|||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
<option name="RIGHT_MARGIN" value="100" />
|
<option name="RIGHT_MARGIN" value="100" />
|
||||||
|
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||||
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
<AndroidXmlCodeStyleSettings>
|
<AndroidXmlCodeStyleSettings>
|
||||||
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||||
</AndroidXmlCodeStyleSettings>
|
</AndroidXmlCodeStyleSettings>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.api.privategroup.invitation;
|
package org.briarproject.api.privategroup.invitation;
|
||||||
|
|
||||||
|
import org.briarproject.api.clients.ProtocolStateException;
|
||||||
import org.briarproject.api.clients.SessionId;
|
import org.briarproject.api.clients.SessionId;
|
||||||
import org.briarproject.api.contact.Contact;
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.contact.ContactId;
|
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
|
* Sends an invitation to share the given private group with the given
|
||||||
* contact, including an optional message.
|
* 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,
|
void sendInvitation(GroupId g, ContactId c, @Nullable String message,
|
||||||
long timestamp, byte[] signature) throws DbException;
|
long timestamp, byte[] signature) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responds to a pending private group invitation from the given contact.
|
* 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)
|
void respondToInvitation(ContactId c, PrivateGroup g, boolean accept)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responds to a pending private group invitation from the given contact.
|
* 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)
|
void respondToInvitation(ContactId c, SessionId s, boolean accept)
|
||||||
throws DbException;
|
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
|
* Returns all private group invitation messages related to the given
|
||||||
* contact.
|
* contact.
|
||||||
|
|||||||
@@ -443,21 +443,26 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
|
|||||||
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
||||||
BdfList members = meta.getList(GROUP_KEY_MEMBERS);
|
BdfList members = meta.getList(GROUP_KEY_MEMBERS);
|
||||||
Visibility v = INVISIBLE;
|
Visibility v = INVISIBLE;
|
||||||
boolean foundMember = false;
|
boolean foundMember = false, changed = false;
|
||||||
for (int i = 0 ; i < members.size(); i++) {
|
for (int i = 0 ; i < members.size(); i++) {
|
||||||
BdfDictionary d = members.getDictionary(i);
|
BdfDictionary d = members.getDictionary(i);
|
||||||
AuthorId memberId = new AuthorId(d.getRaw(KEY_MEMBER_ID));
|
AuthorId memberId = new AuthorId(d.getRaw(KEY_MEMBER_ID));
|
||||||
if (a.equals(memberId)) {
|
if (a.equals(memberId)) {
|
||||||
foundMember = true;
|
foundMember = true;
|
||||||
Visibility vOld = getVisibility(d);
|
// Don't update the visibility if the contact is already visible
|
||||||
if (vOld != INVISIBLE) throw new ProtocolStateException();
|
if (getVisibility(d) == INVISIBLE) {
|
||||||
v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US;
|
changed = true;
|
||||||
d.put(GROUP_KEY_VISIBILITY, v.getInt());
|
v = byContact ? REVEALED_BY_CONTACT : REVEALED_BY_US;
|
||||||
|
d.put(GROUP_KEY_VISIBILITY, v.getInt());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!foundMember) throw new ProtocolStateException();
|
if (!foundMember) throw new ProtocolStateException();
|
||||||
clientHelper.mergeGroupMetadata(txn, g, meta);
|
if (changed) {
|
||||||
txn.attach(new ContactRelationshipRevealedEvent(g, v));
|
clientHelper.mergeGroupMetadata(txn, g, meta);
|
||||||
|
txn.attach(new ContactRelationshipRevealedEvent(g, v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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,
|
private <S extends Session> S handleAction(Transaction txn,
|
||||||
LocalAction type, S session, ProtocolEngine<S> engine)
|
LocalAction type, S session, ProtocolEngine<S> engine)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
@@ -435,9 +459,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
|||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
// If there's no session, the contact can be invited
|
// If there's no session, the contact can be invited
|
||||||
if (ss == null) return true;
|
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
|
// If the session's in the start state, the contact can be invited
|
||||||
CreatorSession session = sessionParser
|
CreatorSession session = sessionParser
|
||||||
.parseCreatorSession(contactGroupId, ss.bdfSession);
|
.parseCreatorSession(contactGroupId, ss.bdfSession);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.briarproject.privategroup.invitation;
|
|||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.ProtocolStateException;
|
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.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
@@ -179,6 +181,7 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
|||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e); // Invalid group metadata
|
throw new DbException(e); // Invalid group metadata
|
||||||
}
|
}
|
||||||
|
// The relationship is already marked visible to the group
|
||||||
// Move to the BOTH_JOINED state
|
// Move to the BOTH_JOINED state
|
||||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||||
@@ -228,6 +231,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
|||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e); // Invalid group metadata
|
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
|
// Move to the BOTH_JOINED state
|
||||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||||
@@ -254,6 +263,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
|||||||
Message sent = sendJoinMessage(txn, s, false);
|
Message sent = sendJoinMessage(txn, s, false);
|
||||||
// Start syncing the private group with the contact
|
// Start syncing the private group with the contact
|
||||||
syncPrivateGroupWithContact(txn, s, true);
|
syncPrivateGroupWithContact(txn, s, true);
|
||||||
|
// Mark the relationship visible to the group, revealed by contact
|
||||||
|
relationshipRevealed(txn, s, true);
|
||||||
// Move to the BOTH_JOINED state
|
// Move to the BOTH_JOINED state
|
||||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||||
sent.getId(), m.getId(), sent.getTimestamp(), BOTH_JOINED);
|
sent.getId(), m.getId(), sent.getTimestamp(), BOTH_JOINED);
|
||||||
@@ -266,6 +277,8 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
|||||||
return abort(txn, s);
|
return abort(txn, s);
|
||||||
// Start syncing the private group with the contact
|
// Start syncing the private group with the contact
|
||||||
syncPrivateGroupWithContact(txn, s, true);
|
syncPrivateGroupWithContact(txn, s, true);
|
||||||
|
// Mark the relationship visible to the group, revealed by us
|
||||||
|
relationshipRevealed(txn, s, false);
|
||||||
// Move to the BOTH_JOINED state
|
// Move to the BOTH_JOINED state
|
||||||
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
return new PeerSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||||
@@ -321,4 +334,12 @@ class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
|
|||||||
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
|
||||||
ERROR);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user