Add support for revealing contacts to the PrivateGroupManager

This also adds two integration tests and improves some small details
This commit is contained in:
Torsten Grote
2016-11-08 22:26:56 -02:00
parent b20c107010
commit ec8982438a
13 changed files with 402 additions and 156 deletions

View File

@@ -11,6 +11,7 @@ import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair; import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
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;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
@@ -20,6 +21,7 @@ import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.privategroup.GroupMember;
import org.briarproject.api.privategroup.GroupMessage; import org.briarproject.api.privategroup.GroupMessage;
import org.briarproject.api.privategroup.GroupMessageFactory; import org.briarproject.api.privategroup.GroupMessageFactory;
import org.briarproject.api.privategroup.GroupMessageHeader; import org.briarproject.api.privategroup.GroupMessageHeader;
@@ -59,6 +61,10 @@ import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY; import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.TestUtils.getRandomBytes; import static org.briarproject.TestUtils.getRandomBytes;
import static org.briarproject.api.identity.Author.Status.VERIFIED; import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_YOU;
import static org.briarproject.api.privategroup.Visibility.VISIBLE;
import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID; import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID; import static org.briarproject.api.sync.ValidationManager.State.INVALID;
@@ -69,13 +75,16 @@ import static org.junit.Assert.assertTrue;
public class PrivateGroupManagerTest extends BriarIntegrationTest { public class PrivateGroupManagerTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1; private LifecycleManager lifecycleManager0, lifecycleManager1,
private SyncSessionFactory sync0, sync1; lifecycleManager2;
private PrivateGroupManager groupManager0, groupManager1; private SyncSessionFactory sync0, sync1, sync2;
private ContactManager contactManager0, contactManager1; private PrivateGroupManager groupManager0, groupManager1, groupManager2;
private ContactId contactId0, contactId1; private ContactManager contactManager0, contactManager1, contactManager2;
private IdentityManager identityManager0, identityManager1; private ContactId contactId01, contactId02, contactId1, contactId2;
private LocalAuthor author0, author1; private IdentityManager identityManager0, identityManager1,
identityManager2;
private LocalAuthor author0, author1, author2;
private DatabaseComponent db0, db1, db2;
private PrivateGroup privateGroup0; private PrivateGroup privateGroup0;
private GroupId groupId0; private GroupId groupId0;
@@ -101,13 +110,14 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
private final File testDir = TestUtils.getTestDirectory(); private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey(); private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000; private final int TIMEOUT = 15000;
private final String AUTHOR0 = "Author 0";
private final String AUTHOR1 = "Author 1"; private final String AUTHOR1 = "Author 1";
private final String AUTHOR2 = "Author 2"; private final String AUTHOR2 = "Author 2";
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(PrivateGroupManagerTest.class.getName()); Logger.getLogger(PrivateGroupManagerTest.class.getName());
private PrivateGroupManagerTestComponent t0, t1; private PrivateGroupManagerTestComponent t0, t1, t2;
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
@@ -117,26 +127,36 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
PrivateGroupManagerTestComponent component = PrivateGroupManagerTestComponent component =
DaggerPrivateGroupManagerTestComponent.builder().build(); DaggerPrivateGroupManagerTestComponent.builder().build();
component.inject(this); component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs()); assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, AUTHOR1); File t0Dir = new File(testDir, AUTHOR0);
t0 = DaggerPrivateGroupManagerTestComponent.builder() t0 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build(); .testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0); injectEagerSingletons(t0);
File t1Dir = new File(testDir, AUTHOR2); File t1Dir = new File(testDir, AUTHOR1);
t1 = DaggerPrivateGroupManagerTestComponent.builder() t1 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build(); .testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1); injectEagerSingletons(t1);
File t2Dir = new File(testDir, AUTHOR2);
t2 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
injectEagerSingletons(t2);
identityManager0 = t0.getIdentityManager(); identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager(); identityManager1 = t1.getIdentityManager();
identityManager2 = t2.getIdentityManager();
contactManager0 = t0.getContactManager(); contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager(); contactManager1 = t1.getContactManager();
contactManager2 = t2.getContactManager();
db0 = t0.getDatabaseComponent();
db1 = t1.getDatabaseComponent();
db2 = t2.getDatabaseComponent();
groupManager0 = t0.getPrivateGroupManager(); groupManager0 = t0.getPrivateGroupManager();
groupManager1 = t1.getPrivateGroupManager(); groupManager1 = t1.getPrivateGroupManager();
groupManager2 = t2.getPrivateGroupManager();
sync0 = t0.getSyncSessionFactory(); sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory(); sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test // initialize waiters fresh for each test
validationWaiter = new Waiter(); validationWaiter = new Waiter();
@@ -328,12 +348,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // make group visible to 1
Transaction txn0 = t0.getDatabaseComponent().startTransaction(false); Transaction txn0 = db0.startTransaction(false);
t0.getDatabaseComponent() db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true);
.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), db0.commitTransaction(txn0);
true); db0.endTransaction(txn0);
t0.getDatabaseComponent().commitTransaction(txn0);
t0.getDatabaseComponent().endTransaction(txn0);
// author1 joins privateGroup0 with wrong timestamp // author1 joins privateGroup0 with wrong timestamp
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
@@ -353,12 +371,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
groupManager1.getPreviousMsgId(groupId0)); groupManager1.getPreviousMsgId(groupId0));
// make group visible to 0 // make group visible to 0
Transaction txn1 = t1.getDatabaseComponent().startTransaction(false); Transaction txn1 = db1.startTransaction(false);
t1.getDatabaseComponent() db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true);
.setVisibleToContact(txn1, contactId0, privateGroup0.getId(), db1.commitTransaction(txn1);
true); db1.endTransaction(txn1);
t1.getDatabaseComponent().commitTransaction(txn1);
t1.getDatabaseComponent().endTransaction(txn1);
// sync join messages // sync join messages
sync0To1(); sync0To1();
@@ -399,12 +415,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // make group visible to 1
Transaction txn0 = t0.getDatabaseComponent().startTransaction(false); Transaction txn0 = db0.startTransaction(false);
t0.getDatabaseComponent() db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true);
.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), db0.commitTransaction(txn0);
true); db0.endTransaction(txn0);
t0.getDatabaseComponent().commitTransaction(txn0);
t0.getDatabaseComponent().endTransaction(txn0);
// author1 joins privateGroup0 with wrong signature in join message // author1 joins privateGroup0 with wrong signature in join message
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
@@ -424,12 +438,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
groupManager1.getPreviousMsgId(groupId0)); groupManager1.getPreviousMsgId(groupId0));
// make group visible to 0 // make group visible to 0
Transaction txn1 = t1.getDatabaseComponent().startTransaction(false); Transaction txn1 = db1.startTransaction(false);
t1.getDatabaseComponent() db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true);
.setVisibleToContact(txn1, contactId0, privateGroup0.getId(), db1.commitTransaction(txn1);
true); db1.endTransaction(txn1);
t1.getDatabaseComponent().commitTransaction(txn1);
t1.getDatabaseComponent().endTransaction(txn1);
// sync join messages // sync join messages
sync0To1(); sync0To1();
@@ -445,6 +457,119 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
assertEquals(1, groupManager0.getHeaders(groupId0).size()); assertEquals(1, groupManager0.getHeaders(groupId0).size());
} }
@Test
public void testGetMembers() throws Exception {
defaultInit();
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
assertEquals(2, members0.size());
for (GroupMember m : members0) {
if (m.getAuthor().equals(author0)) {
assertEquals(VISIBLE, m.getVisibility());
} else {
assertEquals(author1, m.getAuthor());
assertEquals(VISIBLE, m.getVisibility());
}
}
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
assertEquals(2, members1.size());
for (GroupMember m : members0) {
if (m.getAuthor().equals(author1)) {
assertEquals(VISIBLE, m.getVisibility());
} else {
assertEquals(author0, m.getAuthor());
assertEquals(VISIBLE, m.getVisibility());
}
}
}
@Test
public void testRevealingRelationships() throws Exception {
defaultInit();
// make group visible to 2
Transaction txn0 = db0.startTransaction(false);
db0.setVisibleToContact(txn0, contactId2, privateGroup0.getId(), true);
db0.commitTransaction(txn0);
db0.endTransaction(txn0);
// author2 joins privateGroup0
long joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1;
Group invitationGroup = contactGroupFactory
.createContactGroup(CLIENT_ID, author0.getId(),
author2.getId());
BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
privateGroup0.getId());
byte[] creatorSignature =
clientHelper.sign(toSign, author0.getPrivateKey());
GroupMessage joinMsg2 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author2,
inviteTime, creatorSignature);
Transaction txn2 = db2.startTransaction(false);
groupManager2.addPrivateGroup(txn2, privateGroup0, joinMsg2);
// make group visible to 0
db2.setVisibleToContact(txn2, contactId01, privateGroup0.getId(), true);
db2.commitTransaction(txn2);
db2.endTransaction(txn2);
// sync join messages
deliverMessage(sync2, contactId2, sync0, contactId02, "2 to 0");
deliveryWaiter.await(TIMEOUT, 1);
deliverMessage(sync0, contactId02, sync2, contactId2, "0 to 2");
deliveryWaiter.await(TIMEOUT, 2);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// check that everybody sees everybody else as joined
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
assertEquals(3, members0.size());
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
assertEquals(3, members1.size());
Collection<GroupMember> members2 = groupManager2.getMembers(groupId0);
assertEquals(3, members2.size());
// assert that contact relationship is not revealed initially
for (GroupMember m : members1) {
if (m.getAuthor().equals(author2)) {
assertEquals(INVISIBLE, m.getVisibility());
}
}
for (GroupMember m : members2) {
if (m.getAuthor().equals(author1)) {
assertEquals(INVISIBLE, m.getVisibility());
}
}
// reveal contact relationship
Transaction txn1 = db1.startTransaction(false);
groupManager1
.relationshipRevealed(txn1, groupId0, author2.getId(), false);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
txn2 = db2.startTransaction(false);
groupManager2
.relationshipRevealed(txn2, groupId0, author1.getId(), true);
db2.commitTransaction(txn2);
db2.endTransaction(txn2);
// assert that contact relationship is now revealed properly
members1 = groupManager1.getMembers(groupId0);
for (GroupMember m : members1) {
if (m.getAuthor().equals(author2)) {
assertEquals(REVEALED_BY_YOU, m.getVisibility());
}
}
members2 = groupManager2.getMembers(groupId0);
for (GroupMember m : members2) {
if (m.getAuthor().equals(author1)) {
assertEquals(REVEALED_BY_CONTACT, m.getVisibility());
}
}
}
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
stopLifecycles(); stopLifecycles();
@@ -483,7 +608,7 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
byte[] publicKey0 = keyPair0.getPublic().getEncoded(); byte[] publicKey0 = keyPair0.getPublic().getEncoded();
byte[] privateKey0 = keyPair0.getPrivate().getEncoded(); byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
author0 = authorFactory author0 = authorFactory
.createLocalAuthor(AUTHOR1, publicKey0, privateKey0); .createLocalAuthor(AUTHOR0, publicKey0, privateKey0);
identityManager0.registerLocalAuthor(author0); identityManager0.registerLocalAuthor(author0);
privateGroup0 = privateGroup0 =
privateGroupFactory.createPrivateGroup("Testgroup", author0); privateGroupFactory.createPrivateGroup("Testgroup", author0);
@@ -493,21 +618,34 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
byte[] publicKey1 = keyPair1.getPublic().getEncoded(); byte[] publicKey1 = keyPair1.getPublic().getEncoded();
byte[] privateKey1 = keyPair1.getPrivate().getEncoded(); byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
author1 = authorFactory author1 = authorFactory
.createLocalAuthor(AUTHOR2, publicKey1, privateKey1); .createLocalAuthor(AUTHOR1, publicKey1, privateKey1);
identityManager1.registerLocalAuthor(author1); identityManager1.registerLocalAuthor(author1);
KeyPair keyPair2 = crypto.generateSignatureKeyPair();
byte[] publicKey2 = keyPair2.getPublic().getEncoded();
byte[] privateKey2 = keyPair2.getPrivate().getEncoded();
author2 = authorFactory
.createLocalAuthor(AUTHOR2, publicKey2, privateKey2);
identityManager2.registerLocalAuthor(author2);
} }
private void addDefaultContacts() throws DbException { private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact // creator adds invitee as contact
contactId1 = contactManager0.addContact(author1, contactId1 = contactManager0
author0.getId(), master, clock.currentTimeMillis(), true, .addContact(author1, author0.getId(), master,
true, true clock.currentTimeMillis(), true, true, true);
); // invitee adds creator back
// invitee adds sharer back contactId01 = contactManager1
contactId0 = contactManager1.addContact(author0, .addContact(author0, author1.getId(), master,
author1.getId(), master, clock.currentTimeMillis(), true, clock.currentTimeMillis(), true, true, true);
true, true // creator adds invitee as contact
); contactId2 = contactManager0
.addContact(author2, author0.getId(), master,
clock.currentTimeMillis(), true, true, true);
// invitee adds creator back
contactId02 = contactManager2
.addContact(author0, author2.getId(), master,
clock.currentTimeMillis(), true, true, true);
} }
private void listenToEvents() { private void listenToEvents() {
@@ -515,6 +653,8 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
t0.getEventBus().addListener(listener0); t0.getEventBus().addListener(listener0);
Listener listener1 = new Listener(); Listener listener1 = new Listener();
t1.getEventBus().addListener(listener1); t1.getEventBus().addListener(listener1);
Listener listener2 = new Listener();
t2.getEventBus().addListener(listener2);
} }
private void addGroup() throws Exception { private void addGroup() throws Exception {
@@ -527,12 +667,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
// make group visible to 1 // make group visible to 1
Transaction txn0 = t0.getDatabaseComponent().startTransaction(false); Transaction txn0 = db0.startTransaction(false);
t0.getDatabaseComponent() db0.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), true);
.setVisibleToContact(txn0, contactId1, privateGroup0.getId(), db0.commitTransaction(txn0);
true); db0.endTransaction(txn0);
t0.getDatabaseComponent().commitTransaction(txn0);
t0.getDatabaseComponent().endTransaction(txn0);
// author1 joins privateGroup0 // author1 joins privateGroup0
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
@@ -547,17 +685,15 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
GroupMessage joinMsg1 = groupMessageFactory GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1, .createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature); inviteTime, creatorSignature);
groupManager1.addPrivateGroup(privateGroup0, joinMsg1); Transaction txn1 = db1.startTransaction(false);
assertEquals(joinMsg1.getMessage().getId(), groupManager1.addPrivateGroup(txn1, privateGroup0, joinMsg1);
groupManager1.getPreviousMsgId(groupId0));
// make group visible to 0 // make group visible to 0
Transaction txn1 = t1.getDatabaseComponent().startTransaction(false); db1.setVisibleToContact(txn1, contactId01, privateGroup0.getId(), true);
t1.getDatabaseComponent() db1.commitTransaction(txn1);
.setVisibleToContact(txn1, contactId0, privateGroup0.getId(), db1.endTransaction(txn1);
true); assertEquals(joinMsg1.getMessage().getId(),
t1.getDatabaseComponent().commitTransaction(txn1); groupManager1.getPreviousMsgId(groupId0));
t1.getDatabaseComponent().endTransaction(txn1);
// sync join messages // sync join messages
sync0To1(); sync0To1();
@@ -567,11 +703,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
} }
private void sync0To1() throws IOException, TimeoutException { private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1"); deliverMessage(sync0, contactId01, sync1, contactId1, "0 to 1");
} }
private void sync1To0() throws IOException, TimeoutException { private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0"); deliverMessage(sync1, contactId1, sync0, contactId01, "1 to 0");
} }
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId, private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
@@ -600,18 +736,23 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// Start the lifecycle manager and wait for it to finish // Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager(); lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager(); lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager0.startServices(AUTHOR1); lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager1.startServices(AUTHOR2); lifecycleManager0.startServices(AUTHOR0);
lifecycleManager1.startServices(AUTHOR1);
lifecycleManager2.startServices(AUTHOR2);
lifecycleManager0.waitForStartup(); lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup(); lifecycleManager1.waitForStartup();
lifecycleManager2.waitForStartup();
} }
private void stopLifecycles() throws InterruptedException { private void stopLifecycles() throws InterruptedException {
// Clean up // Clean up
lifecycleManager0.stopServices(); lifecycleManager0.stopServices();
lifecycleManager1.stopServices(); lifecycleManager1.stopServices();
lifecycleManager2.stopServices();
lifecycleManager0.waitForShutdown(); lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown(); lifecycleManager1.waitForShutdown();
lifecycleManager2.waitForShutdown();
} }
private void injectEagerSingletons( private void injectEagerSingletons(

View File

@@ -105,7 +105,7 @@ public class GroupActivity extends
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
actionBar.setSubtitle(getString(R.string.groups_created_by, actionBar.setSubtitle(getString(R.string.groups_created_by,
group.getAuthor().getName())); group.getCreator().getName()));
} }
controller.isCreator(group, controller.isCreator(group,
new UiResultExceptionHandler<Boolean, DbException>(this) { new UiResultExceptionHandler<Boolean, DbException>(this) {

View File

@@ -196,7 +196,7 @@ public class GroupControllerImpl extends
try { try {
LocalAuthor author = identityManager.getLocalAuthor(); LocalAuthor author = identityManager.getLocalAuthor();
boolean isCreator = boolean isCreator =
author.getId().equals(group.getAuthor().getId()); author.getId().equals(group.getCreator().getId());
handler.onResult(isCreator); handler.onResult(isCreator);
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))

View File

@@ -43,7 +43,7 @@ class GroupItem {
} }
Author getCreator() { Author getCreator() {
return privateGroup.getAuthor(); return privateGroup.getCreator();
} }
String getName() { String getName() {

View File

@@ -7,6 +7,8 @@ import org.briarproject.api.privategroup.GroupMember;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class MemberListItem { class MemberListItem {
@@ -17,7 +19,7 @@ class MemberListItem {
public MemberListItem(GroupMember groupMember) { public MemberListItem(GroupMember groupMember) {
this.member = groupMember.getAuthor(); this.member = groupMember.getAuthor();
this.sharing = groupMember.isShared(); this.sharing = groupMember.getVisibility() != INVISIBLE; // TODO #732
this.status = groupMember.getStatus(); this.status = groupMember.getStatus();
} }

View File

@@ -12,12 +12,12 @@ public class GroupMember {
private final Author author; private final Author author;
private final Status status; private final Status status;
private final boolean shared; private final Visibility visibility;
public GroupMember(Author author, Status status, boolean shared) { public GroupMember(Author author, Status status, Visibility visibility) {
this.author = author; this.author = author;
this.status = status; this.status = status;
this.shared = shared; this.visibility = visibility;
} }
public Author getAuthor() { public Author getAuthor() {
@@ -28,8 +28,8 @@ public class GroupMember {
return status; return status;
} }
public boolean isShared() { public Visibility getVisibility() {
return shared; return visibility;
} }
} }

View File

@@ -12,20 +12,22 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class PrivateGroup extends NamedGroup implements Shareable { public class PrivateGroup extends NamedGroup implements Shareable {
private final Author author; private final Author creator;
public PrivateGroup(Group group, String name, Author author, byte[] salt) { public PrivateGroup(Group group, String name, Author creator, byte[] salt) {
super(group, name, salt); super(group, name, salt);
this.author = author; this.creator = creator;
} }
public Author getAuthor() { public Author getCreator() {
return author; return creator;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof PrivateGroup && super.equals(o); return o instanceof PrivateGroup &&
creator.equals(((PrivateGroup) o).getCreator()) &&
super.equals(o);
} }
} }

View File

@@ -1,9 +1,11 @@
package org.briarproject.api.privategroup; package org.briarproject.api.privategroup;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.MessageTracker; import org.briarproject.api.clients.MessageTracker;
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.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
@@ -20,16 +22,16 @@ public interface PrivateGroupManager extends MessageTracker {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup");
/** /**
* Adds a new private group and joins it. * Adds a new private group and joins it as the creator.
* *
* @param group The private group to add * @param group The private group to add
* @param joinMsg The new member's join message * @param joinMsg The creators's join message
*/ */
void addPrivateGroup(PrivateGroup group, GroupMessage joinMsg) void addPrivateGroup(PrivateGroup group, GroupMessage joinMsg)
throws DbException; throws DbException;
/** /**
* Adds a new private group and joins it. * Adds a new private group and joins it as a member.
* *
* @param group The private group to add * @param group The private group to add
* @param joinMsg The new member's join message * @param joinMsg The new member's join message
@@ -103,6 +105,17 @@ public interface PrivateGroupManager extends MessageTracker {
*/ */
boolean isMember(Transaction txn, GroupId g, Author a) throws DbException; boolean isMember(Transaction txn, GroupId g, Author a) throws DbException;
/**
* This method needs to be called when a contact relationship
* has been revealed between you and the Author with AuthorId a
* in the Group identified by the GroupId g.
*
* @param byContact true if the remote contact has revealed
* the relationship first. Otherwise false.
*/
void relationshipRevealed(Transaction txn, GroupId g, AuthorId a,
boolean byContact) throws FormatException, DbException;
/** /**
* Registers a hook to be called when members are added * Registers a hook to be called when members are added
* or groups are removed. * or groups are removed.

View File

@@ -0,0 +1,25 @@
package org.briarproject.api.privategroup;
public enum Visibility {
INVISIBLE(0),
VISIBLE(1),
REVEALED_BY_YOU(2),
REVEALED_BY_CONTACT(3);
int value;
Visibility(int value) {
this.value = value;
}
public static Visibility valueOf(int value) {
for (Visibility v : values()) if (v.value == value) return v;
throw new IllegalArgumentException();
}
public int getInt() {
return value;
}
}

View File

@@ -9,13 +9,15 @@ interface Constants {
String KEY_TIMESTAMP = "timestamp"; String KEY_TIMESTAMP = "timestamp";
String KEY_READ = MSG_KEY_READ; String KEY_READ = MSG_KEY_READ;
String KEY_PARENT_MSG_ID = "parentMsgId"; String KEY_PARENT_MSG_ID = "parentMsgId";
String KEY_NEW_MEMBER_MSG_ID = "newMemberMsgId";
String KEY_PREVIOUS_MSG_ID = "previousMsgId"; String KEY_PREVIOUS_MSG_ID = "previousMsgId";
String KEY_MEMBER_ID = "memberId"; String KEY_MEMBER_ID = "memberId";
String KEY_MEMBER_NAME = "memberName"; String KEY_MEMBER_NAME = "memberName";
String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey"; String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey";
String KEY_MEMBERS = "members"; String GROUP_KEY_MEMBERS = "members";
String KEY_DISSOLVED = "dissolved"; String GROUP_KEY_OUR_GROUP = "ourGroup";
String GROUP_KEY_CREATOR_ID = "creatorId";
String GROUP_KEY_DISSOLVED = "dissolved";
String GROUP_KEY_VISIBILITY = "visibility";
} }

View File

@@ -98,7 +98,7 @@ class GroupMessageValidator extends BdfMessageValidator {
PrivateGroup pg = privateGroupFactory.parsePrivateGroup(g); PrivateGroup pg = privateGroupFactory.parsePrivateGroup(g);
// invite is null if the member is the creator of the private group // invite is null if the member is the creator of the private group
Author creator = pg.getAuthor(); Author creator = pg.getCreator();
BdfList invite = body.getOptionalList(3); BdfList invite = body.getOptionalList(3);
if (invite == null) { if (invite == null) {
if (!member.equals(creator)) if (!member.equals(creator))

View File

@@ -2,8 +2,7 @@ package org.briarproject.privategroup;
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.contact.Contact; import org.briarproject.api.clients.ProtocolStateException;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry; import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
@@ -27,6 +26,7 @@ import org.briarproject.api.privategroup.MessageType;
import org.briarproject.api.privategroup.PrivateGroup; import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupFactory; import org.briarproject.api.privategroup.PrivateGroupFactory;
import org.briarproject.api.privategroup.PrivateGroupManager; import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.privategroup.Visibility;
import org.briarproject.api.sync.Group; 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;
@@ -48,12 +48,17 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.identity.Author.Status.OURSELVES; import static org.briarproject.api.identity.Author.Status.OURSELVES;
import static org.briarproject.api.identity.Author.Status.UNVERIFIED;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.privategroup.MessageType.JOIN; import static org.briarproject.api.privategroup.MessageType.JOIN;
import static org.briarproject.api.privategroup.MessageType.POST; import static org.briarproject.api.privategroup.MessageType.POST;
import static org.briarproject.privategroup.Constants.KEY_DISSOLVED; import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.privategroup.Constants.KEY_MEMBERS; import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_YOU;
import static org.briarproject.api.privategroup.Visibility.VISIBLE;
import static org.briarproject.privategroup.Constants.GROUP_KEY_CREATOR_ID;
import static org.briarproject.privategroup.Constants.GROUP_KEY_DISSOLVED;
import static org.briarproject.privategroup.Constants.GROUP_KEY_MEMBERS;
import static org.briarproject.privategroup.Constants.GROUP_KEY_OUR_GROUP;
import static org.briarproject.privategroup.Constants.GROUP_KEY_VISIBILITY;
import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID; import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID;
import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME; import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME;
import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY; import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY;
@@ -88,7 +93,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
throws DbException { throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
addPrivateGroup(txn, group, joinMsg); addPrivateGroup(txn, group, joinMsg, true);
db.commitTransaction(txn); db.commitTransaction(txn);
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
@@ -98,11 +103,19 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override @Override
public void addPrivateGroup(Transaction txn, PrivateGroup group, public void addPrivateGroup(Transaction txn, PrivateGroup group,
GroupMessage joinMsg) throws DbException { GroupMessage joinMsg) throws DbException {
addPrivateGroup(txn, group, joinMsg, false);
}
private void addPrivateGroup(Transaction txn, PrivateGroup group,
GroupMessage joinMsg, boolean creator) throws DbException {
try { try {
db.addGroup(txn, group.getGroup()); db.addGroup(txn, group.getGroup());
AuthorId creatorId = joinMsg.getMember().getId();
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_MEMBERS, new BdfList()), new BdfEntry(GROUP_KEY_MEMBERS, new BdfList()),
new BdfEntry(KEY_DISSOLVED, false) new BdfEntry(GROUP_KEY_CREATOR_ID, creatorId),
new BdfEntry(GROUP_KEY_OUR_GROUP, creator),
new BdfEntry(GROUP_KEY_DISSOLVED, false)
); );
clientHelper.mergeGroupMetadata(txn, group.getId(), meta); clientHelper.mergeGroupMetadata(txn, group.getId(), meta);
joinPrivateGroup(txn, joinMsg); joinPrivateGroup(txn, joinMsg);
@@ -118,7 +131,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
addMessageMetadata(meta, m, true); addMessageMetadata(meta, m, true);
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true); clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
trackOutgoingMessage(txn, m.getMessage()); trackOutgoingMessage(txn, m.getMessage());
addMember(txn, m.getMessage().getGroupId(), m.getMember()); addMember(txn, m.getMessage().getGroupId(), m.getMember(), VISIBLE);
setPreviousMsgId(txn, m.getMessage().getGroupId(), setPreviousMsgId(txn, m.getMessage().getGroupId(),
m.getMessage().getId()); m.getMessage().getId());
} }
@@ -171,7 +184,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
public void markGroupDissolved(Transaction txn, GroupId g) public void markGroupDissolved(Transaction txn, GroupId g)
throws DbException { throws DbException {
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_DISSOLVED, true) new BdfEntry(GROUP_KEY_DISSOLVED, true)
); );
try { try {
clientHelper.mergeGroupMetadata(txn, g, meta); clientHelper.mergeGroupMetadata(txn, g, meta);
@@ -283,7 +296,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
public boolean isDissolved(GroupId g) throws DbException { public boolean isDissolved(GroupId g) throws DbException {
try { try {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(g); BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(g);
return meta.getBoolean(KEY_DISSOLVED); return meta.getBoolean(GROUP_KEY_DISSOLVED);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -376,18 +389,11 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
Collection<GroupMember> members = new ArrayList<GroupMember>(); Collection<GroupMember> members = new ArrayList<GroupMember>();
Collection<Author> authors = getMembers(txn, g); Map<Author, Visibility> authors = getMembers(txn, g);
for (Author a : authors) { for (Entry<Author, Visibility> m : authors.entrySet()) {
Status status = identityManager.getAuthorStatus(txn, a.getId()); Status status = identityManager
boolean shared = false; .getAuthorStatus(txn, m.getKey().getId());
if (status == VERIFIED || status == UNVERIFIED) { members.add(new GroupMember(m.getKey(), status, m.getValue()));
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, a.getId());
if (contacts.size() != 1) throw new DbException();
ContactId c = contacts.iterator().next().getId();
shared = db.isVisibleToContact(txn, c, g);
}
members.add(new GroupMember(a, status, shared));
} }
db.commitTransaction(txn); db.commitTransaction(txn);
return members; return members;
@@ -396,17 +402,19 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
} }
} }
private Collection<Author> getMembers(Transaction txn, GroupId g) private Map<Author, Visibility> getMembers(Transaction txn, GroupId g)
throws DbException { throws DbException {
try { try {
Collection<Author> members = new ArrayList<Author>();
BdfDictionary meta = BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g); clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList list = meta.getList(KEY_MEMBERS); BdfList list = meta.getList(GROUP_KEY_MEMBERS);
Map<Author, Visibility> members =
new HashMap<Author, Visibility>(list.size());
for (Object o : list) { for (Object o : list) {
BdfDictionary d = (BdfDictionary) o; BdfDictionary d = (BdfDictionary) o;
Author member = getAuthor(d); Author member = getAuthor(d);
members.add(member); Visibility v = getVisibility(d);
members.put(member, v);
} }
return members; return members;
} catch (FormatException e) { } catch (FormatException e) {
@@ -417,12 +425,34 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override @Override
public boolean isMember(Transaction txn, GroupId g, Author a) public boolean isMember(Transaction txn, GroupId g, Author a)
throws DbException { throws DbException {
for (Author member : getMembers(txn, g)) { for (Author member : getMembers(txn, g).keySet()) {
if (member.equals(a)) return true; if (member.equals(a)) return true;
} }
return false; return false;
} }
@Override
public void relationshipRevealed(Transaction txn, GroupId g, AuthorId a,
boolean byContact) throws FormatException, DbException {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList members = meta.getList(GROUP_KEY_MEMBERS);
boolean foundMember = false;
for (Object o : members) {
BdfDictionary d = (BdfDictionary) o;
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();
Visibility vNew =
byContact ? REVEALED_BY_CONTACT : REVEALED_BY_YOU;
d.put(GROUP_KEY_VISIBILITY, vNew.getInt());
}
}
if (!foundMember) throw new ProtocolStateException();
clientHelper.mergeGroupMetadata(txn, g, meta);
}
@Override @Override
public void registerPrivateGroupHook(PrivateGroupHook hook) { public void registerPrivateGroupHook(PrivateGroupHook hook) {
hooks.add(hook); hooks.add(hook);
@@ -432,47 +462,14 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
protected boolean incomingMessage(Transaction txn, Message m, BdfList body, protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException { BdfDictionary meta) throws DbException, FormatException {
long timestamp = meta.getLong(KEY_TIMESTAMP);
MessageType type = MessageType type =
MessageType.valueOf(meta.getLong(KEY_TYPE).intValue()); MessageType.valueOf(meta.getLong(KEY_TYPE).intValue());
switch (type) { switch (type) {
case JOIN: case JOIN:
addMember(txn, m.getGroupId(), getAuthor(meta)); handleJoinMessage(txn, m, meta);
trackIncomingMessage(txn, m);
attachGroupMessageAddedEvent(txn, m, meta, false);
return true; return true;
case POST: case POST:
// timestamp must be greater than the timestamps of parent post handleGroupMessage(txn, m, meta);
byte[] parentIdBytes = meta.getOptionalRaw(KEY_PARENT_MSG_ID);
if (parentIdBytes != null) {
MessageId parentId = new MessageId(parentIdBytes);
BdfDictionary parentMeta = clientHelper
.getMessageMetadataAsDictionary(txn, parentId);
if (timestamp <= parentMeta.getLong(KEY_TIMESTAMP))
throw new FormatException();
MessageType parentType = MessageType
.valueOf(parentMeta.getLong(KEY_TYPE).intValue());
if (parentType != POST)
throw new FormatException();
}
// and the member's previous message
byte[] previousMsgIdBytes = meta.getRaw(KEY_PREVIOUS_MSG_ID);
MessageId previousMsgId = new MessageId(previousMsgIdBytes);
BdfDictionary previousMeta = clientHelper
.getMessageMetadataAsDictionary(txn, previousMsgId);
if (timestamp <= previousMeta.getLong(KEY_TIMESTAMP))
throw new FormatException();
// previous message must be from same member
if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
previousMeta.getRaw(KEY_MEMBER_ID)))
throw new FormatException();
// previous message must be a POST or JOIN
MessageType previousType = MessageType
.valueOf(previousMeta.getLong(KEY_TYPE).intValue());
if (previousType != JOIN && previousType != POST)
throw new FormatException();
trackIncomingMessage(txn, m);
attachGroupMessageAddedEvent(txn, m, meta, false);
return true; return true;
default: default:
// the validator should only let valid types pass // the validator should only let valid types pass
@@ -480,6 +477,63 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
} }
} }
private void handleJoinMessage(Transaction txn, Message m,
BdfDictionary meta) throws FormatException, DbException {
// find out if contact relationship is visible and then add new member
Author member = getAuthor(meta);
BdfDictionary groupMeta = clientHelper
.getGroupMetadataAsDictionary(txn, m.getGroupId());
boolean ourGroup = groupMeta.getBoolean(GROUP_KEY_OUR_GROUP);
Visibility v = VISIBLE;
if (!ourGroup) {
AuthorId creatorId = new AuthorId(
groupMeta.getRaw(GROUP_KEY_CREATOR_ID));
if (!creatorId.equals(member.getId()))
v = INVISIBLE;
}
addMember(txn, m.getGroupId(), member, v);
// track message and broadcast event
trackIncomingMessage(txn, m);
attachGroupMessageAddedEvent(txn, m, meta, false);
}
private void handleGroupMessage(Transaction txn, Message m,
BdfDictionary meta) throws FormatException, DbException {
// timestamp must be greater than the timestamps of parent post
long timestamp = meta.getLong(KEY_TIMESTAMP);
byte[] parentIdBytes = meta.getOptionalRaw(KEY_PARENT_MSG_ID);
if (parentIdBytes != null) {
MessageId parentId = new MessageId(parentIdBytes);
BdfDictionary parentMeta = clientHelper
.getMessageMetadataAsDictionary(txn, parentId);
if (timestamp <= parentMeta.getLong(KEY_TIMESTAMP))
throw new FormatException();
MessageType parentType = MessageType
.valueOf(parentMeta.getLong(KEY_TYPE).intValue());
if (parentType != POST)
throw new FormatException();
}
// and the member's previous message
byte[] previousMsgIdBytes = meta.getRaw(KEY_PREVIOUS_MSG_ID);
MessageId previousMsgId = new MessageId(previousMsgIdBytes);
BdfDictionary previousMeta = clientHelper
.getMessageMetadataAsDictionary(txn, previousMsgId);
if (timestamp <= previousMeta.getLong(KEY_TIMESTAMP))
throw new FormatException();
// previous message must be from same member
if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
previousMeta.getRaw(KEY_MEMBER_ID)))
throw new FormatException();
// previous message must be a POST or JOIN
MessageType previousType = MessageType
.valueOf(previousMeta.getLong(KEY_TYPE).intValue());
if (previousType != JOIN && previousType != POST)
throw new FormatException();
// track message and broadcast event
trackIncomingMessage(txn, m);
attachGroupMessageAddedEvent(txn, m, meta, false);
}
private void attachGroupMessageAddedEvent(Transaction txn, Message m, private void attachGroupMessageAddedEvent(Transaction txn, Message m,
BdfDictionary meta, boolean local) BdfDictionary meta, boolean local)
throws DbException, FormatException { throws DbException, FormatException {
@@ -489,15 +543,16 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
txn.attach(e); txn.attach(e);
} }
private void addMember(Transaction txn, GroupId g, Author a) private void addMember(Transaction txn, GroupId g, Author a, Visibility v)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g); BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList members = meta.getList(KEY_MEMBERS); BdfList members = meta.getList(GROUP_KEY_MEMBERS);
members.add(BdfDictionary.of( members.add(BdfDictionary.of(
new BdfEntry(KEY_MEMBER_ID, a.getId()), new BdfEntry(KEY_MEMBER_ID, a.getId()),
new BdfEntry(KEY_MEMBER_NAME, a.getName()), new BdfEntry(KEY_MEMBER_NAME, a.getName()),
new BdfEntry(KEY_MEMBER_PUBLIC_KEY, a.getPublicKey()) new BdfEntry(KEY_MEMBER_PUBLIC_KEY, a.getPublicKey()),
new BdfEntry(GROUP_KEY_VISIBILITY, v.getInt())
)); ));
clientHelper.mergeGroupMetadata(txn, g, meta); clientHelper.mergeGroupMetadata(txn, g, meta);
for (PrivateGroupHook hook : hooks) { for (PrivateGroupHook hook : hooks) {
@@ -512,4 +567,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
return new Author(authorId, name, publicKey); return new Author(authorId, name, publicKey);
} }
private Visibility getVisibility(BdfDictionary meta)
throws FormatException {
return Visibility
.valueOf(meta.getLong(GROUP_KEY_VISIBILITY).intValue());
}
} }

View File

@@ -105,7 +105,7 @@ abstract class AbstractProtocolEngine<S extends Session>
} }
Message m = messageEncoder.encodeInviteMessage( Message m = messageEncoder.encodeInviteMessage(
session.getContactGroupId(), privateGroup.getId(), session.getContactGroupId(), privateGroup.getId(),
timestamp, privateGroup.getName(), privateGroup.getAuthor(), timestamp, privateGroup.getName(), privateGroup.getCreator(),
privateGroup.getSalt(), message, signature); privateGroup.getSalt(), message, signature);
sendMessage(txn, m, INVITE, privateGroup.getId(), true); sendMessage(txn, m, INVITE, privateGroup.getId(), true);
return m; return m;