Verified Commit d3dbcfd6 authored by Torsten Grote's avatar Torsten Grote
Browse files

Recreate plausible private group sharing sessions when re-adding contact from group

parent c4c70f5a
Pipeline #6410 passed with stages
in 11 minutes and 47 seconds
......@@ -110,9 +110,9 @@ public interface PrivateGroupManager {
throws DbException;
/**
* Returns true if the private group with the given ID was created by us.
* Returns true if the given private group was created by us.
*/
boolean isOurPrivateGroup(Transaction txn, GroupId g) throws DbException;
boolean isOurPrivateGroup(Transaction txn, PrivateGroup g) throws DbException;
/**
* Returns the text of the private group message with the given ID.
......
......@@ -287,11 +287,10 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
}
@Override
public boolean isOurPrivateGroup(Transaction txn, GroupId g)
public boolean isOurPrivateGroup(Transaction txn, PrivateGroup g)
throws DbException {
PrivateGroup group = getPrivateGroup(txn, g);
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
return localAuthor.getId().equals(group.getCreator().getId());
return localAuthor.getId().equals(g.getCreator().getId());
}
@Override
......
......@@ -126,20 +126,60 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group
clientHelper.setContactId(txn, g.getId(), c.getId());
// If the contact belongs to any private groups (we didn't create),
// create a peer session
for (Group pg : db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
// If the contact belongs to any private groups, create a peer session
// or sessions in LEFT state for creator/invitee.
for (Group group : db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION)) {
if (privateGroupManager.isMember(txn, pg.getId(), c.getAuthor())) {
if (!privateGroupManager.isOurPrivateGroup(txn, pg.getId())) {
addingMember(txn, pg.getId(), c);
}
if (privateGroupManager
.isMember(txn, group.getId(), c.getAuthor())) {
PrivateGroup pg =
privateGroupManager.getPrivateGroup(txn, group.getId());
recreateSession(txn, c, pg, g.getId());
}
}
}
private void recreateSession(Transaction txn, Contact c,
PrivateGroup pg, GroupId contactGroupId) throws DbException {
boolean isOur = privateGroupManager.isOurPrivateGroup(txn, pg);
boolean isTheirs =
c.getAuthor().getId().equals(pg.getCreator().getId());
if (isOur || isTheirs) {
// we are creator or invitee, create a left session for each role
MessageId storageId = createStorageId(txn, contactGroupId);
Session<?> session;
if (isOur) {
session = new CreatorSession(contactGroupId, pg.getId(), null,
null, 0, 0, CreatorState.LEFT);
} else {
session = new InviteeSession(contactGroupId, pg.getId(), null,
null, 0, 0, InviteeState.LEFT);
}
try {
storeSession(txn, storageId, session);
} catch (FormatException e) {
throw new DbException(e);
}
} else {
// we are neither creator nor invitee, create peer session
addingMember(txn, pg.getId(), c);
}
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
// mark private groups created by that contact as dissolved
for (Group g : db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION)) {
if (privateGroupManager.isMember(txn, g.getId(), c.getAuthor())) {
PrivateGroup pg =
privateGroupManager.getPrivateGroup(txn, g.getId());
// check if contact to be removed is creator of the group
if (c.getAuthor().getId().equals(pg.getCreator().getId())) {
privateGroupManager.markGroupDissolved(txn, g.getId());
}
}
}
// Remove the contact group (all messages will be removed with it)
db.removeGroup(txn, getContactGroup(c));
}
......
......@@ -722,15 +722,26 @@ public class GroupInvitationIntegrationTest
sync0To1(2, true); // + one invitation protocol join message
sync1To0(1, true);
// inviting again is not possible
assertFalse(groupInvitationManager0
.isInvitationAllowed(contact1From0, privateGroup.getId()));
// re-add contacts
// contacts remove each other
removeAllContacts();
// group gets dissolved for invitee automatically, but not creator
assertFalse(groupManager0.isDissolved(privateGroup.getId()));
assertTrue(groupManager1.isDissolved(privateGroup.getId()));
// contacts re-add each other
addDefaultContacts();
assertTrue(groupInvitationManager0
// creator can still not invite again
assertFalse(groupInvitationManager0
.isInvitationAllowed(contact1From0, privateGroup.getId()));
// finally invitee can remove group without issues
groupManager1.removePrivateGroup(privateGroup.getId());
}
private Collection<ConversationMessageHeader> getMessages1From0()
......
......@@ -45,6 +45,8 @@ import java.util.Map;
import javax.annotation.Nullable;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static junit.framework.TestCase.fail;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
......@@ -105,7 +107,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
private final ContactId contactId = contact.getId();
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group privateGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group group = getGroup(CLIENT_ID, MAJOR_VERSION);
private final PrivateGroup privateGroup = new PrivateGroup(group,
getRandomString(5), getAuthor(), getRandomBytes(32));
private final BdfDictionary meta = BdfDictionary.of(new BdfEntry("m", "e"));
private final Message message = getMessage(contactGroup.getId());
private final BdfList body = BdfList.of("body");
......@@ -159,9 +163,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
will(returnValue(false));
oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
will(returnValue(singletonList(contact)));
}});
expectAddingContact(contact);
expectAddingContact(contact, emptyList());
groupInvitationManager.onDatabaseOpened(txn);
}
......@@ -177,7 +181,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
groupInvitationManager.onDatabaseOpened(txn);
}
private void expectAddingContact(Contact c) throws Exception {
private void expectAddingContact(Contact c, Collection<Group> groups)
throws Exception {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, c);
......@@ -192,15 +197,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
.setContactId(txn, contactGroup.getId(), contactId);
oneOf(db).getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
will(returnValue(Collections.singletonList(privateGroup)));
oneOf(privateGroupManager).isMember(txn, privateGroup.getId(),
c.getAuthor());
will(returnValue(true));
oneOf(privateGroupManager)
.isOurPrivateGroup(txn, privateGroup.getId());
will(returnValue(false));
will(returnValue(groups));
}});
expectAddingMember(privateGroup.getId(), c);
}
private void expectAddingMember(GroupId g, Contact c) throws Exception {
......@@ -254,13 +252,99 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test
public void testAddingContact() throws Exception {
expectAddingContact(contact);
expectAddingContact(contact, singletonList(group));
context.checking(new Expectations() {{
oneOf(privateGroupManager)
.isMember(txn, privateGroup.getId(), contact.getAuthor());
will(returnValue(true));
oneOf(privateGroupManager)
.getPrivateGroup(txn, privateGroup.getId());
will(returnValue(privateGroup));
oneOf(privateGroupManager).isOurPrivateGroup(txn, privateGroup);
will(returnValue(false));
}});
// creates PEER session
expectAddingMember(privateGroup.getId(), contact);
groupInvitationManager.addingContact(txn, contact);
}
@Test
public void testAddingContactWhoCreatedGroup() throws Exception {
PrivateGroup privateGroup = new PrivateGroup(group,
getRandomString(5), contact.getAuthor(), getRandomBytes(32));
expectAddingContact(contact, singletonList(group));
context.checking(new Expectations() {{
oneOf(privateGroupManager)
.isMember(txn, privateGroup.getId(), contact.getAuthor());
will(returnValue(true));
oneOf(privateGroupManager)
.getPrivateGroup(txn, privateGroup.getId());
will(returnValue(privateGroup));
oneOf(privateGroupManager).isOurPrivateGroup(txn, privateGroup);
will(returnValue(false));
}});
expectCreateStorageId();
context.checking(new Expectations() {{
oneOf(sessionEncoder)
.encodeSession(with(any(InviteeSession.class)));
will(returnValue(meta));
oneOf(clientHelper)
.mergeMessageMetadata(txn, storageMessage.getId(), meta);
}});
groupInvitationManager.addingContact(txn, contact);
}
@Test
public void testRemovingContact() throws Exception {
public void testRemovingContactWithoutCommonGroups() throws Exception {
context.checking(new Expectations() {{
oneOf(db).getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
will(returnValue(emptyList()));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup);
}});
groupInvitationManager.removingContact(txn, contact);
}
@Test
public void testRemovingContactWithCommonGroups() throws Exception {
context.checking(new Expectations() {{
oneOf(db).getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
will(returnValue(singletonList(group)));
oneOf(privateGroupManager).isMember(txn, group.getId(), author);
will(returnValue(true));
oneOf(privateGroupManager).getPrivateGroup(txn, group.getId());
will(returnValue(privateGroup));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup);
}});
groupInvitationManager.removingContact(txn, contact);
}
@Test
public void testRemovingContactWhoIsCreatorOfCommonGroup()
throws Exception {
PrivateGroup privateGroup = new PrivateGroup(group,
getRandomString(5), contact.getAuthor(), getRandomBytes(32));
context.checking(new Expectations() {{
oneOf(db).getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
will(returnValue(singletonList(group)));
oneOf(privateGroupManager).isMember(txn, group.getId(), author);
will(returnValue(true));
oneOf(privateGroupManager).getPrivateGroup(txn, group.getId());
will(returnValue(privateGroup));
oneOf(privateGroupManager).markGroupDissolved(txn, group.getId());
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
......@@ -350,8 +434,8 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
BdfDictionary bdfSession) throws Exception {
expectParseMessageMetadata();
expectGetSession(oneResult, sessionId, contactGroup.getId());
Session<?> session =
expectHandleMessage(role, messageMetadata, bdfSession, type);
Session<?> session = expectHandleMessage(role, messageMetadata,
bdfSession, type);
expectStoreSession(session, storageMessage.getId());
}
......@@ -564,7 +648,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test
public void testAcceptInvitationWithGroupId() throws Exception {
PrivateGroup pg = new PrivateGroup(privateGroup,
PrivateGroup pg = new PrivateGroup(group,
getRandomString(MAX_GROUP_NAME_LENGTH), author,
getRandomBytes(GROUP_SALT_LENGTH));
......@@ -574,7 +658,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test
public void testDeclineInvitationWithGroupId() throws Exception {
PrivateGroup pg = new PrivateGroup(privateGroup,
PrivateGroup pg = new PrivateGroup(group,
getRandomString(MAX_GROUP_NAME_LENGTH), author,
getRandomBytes(GROUP_SALT_LENGTH));
......@@ -665,7 +749,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
privateGroup.getId(), time1, "name", author,
new byte[0], null, new byte[0], NO_AUTO_DELETE_TIMER);
PrivateGroup pg =
new PrivateGroup(privateGroup, invite.getGroupName(),
new PrivateGroup(group, invite.getGroupName(),
invite.getCreator(), invite.getSalt());
context.checking(new Expectations() {{
......@@ -733,7 +817,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
new InviteMessage(message2.getId(), contactGroup.getId(),
privateGroup.getId(), time2, groupName, author, salt,
null, getRandomBytes(5), NO_AUTO_DELETE_TIMER);
PrivateGroup pg = new PrivateGroup(privateGroup, groupName,
PrivateGroup pg = new PrivateGroup(group, groupName,
author, salt);
context.checking(new Expectations() {{
......@@ -742,7 +826,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
......@@ -834,7 +918,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
expectAddingMember(privateGroup.getId(), contact);
context.checking(new Expectations() {{
oneOf(db).getContactsByAuthorId(txn, author.getId());
will(returnValue(Collections.singletonList(contact)));
will(returnValue(singletonList(contact)));
}});
groupInvitationManager.addingMember(txn, privateGroup.getId(), author);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment