Merge branch '678-private-group-hooks-and-membership' into 'master'

Add methods and hooks to PrivateGroupManager related to members and removal

This MR is the first of two MRs related to #678.

See merge request !377
This commit is contained in:
akwizgran
2016-10-31 16:27:15 +00:00
4 changed files with 196 additions and 12 deletions

View File

@@ -0,0 +1,35 @@
package org.briarproject.api.privategroup;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupMember {
private final Author author;
private final Status status;
private final boolean shared;
public GroupMember(Author author, Status status, boolean shared) {
this.author = author;
this.status = status;
this.shared = shared;
}
public Author getAuthor() {
return author;
}
public Status getStatus() {
return status;
}
public boolean isShared() {
return shared;
}
}

View File

@@ -3,12 +3,15 @@ package org.briarproject.api.privategroup;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import java.util.Collection;
@NotNullByDefault
public interface PrivateGroupManager extends MessageTracker {
/** Returns the unique ID of the private group client. */
@@ -35,6 +38,12 @@ public interface PrivateGroupManager extends MessageTracker {
// TODO change to getPreviousMessageHeader()
long getMessageTimestamp(MessageId id) throws DbException;
/** Marks the group with GroupId g as resolved */
void markGroupDissolved(Transaction txn, GroupId g) throws DbException;
/** Returns true if the private group has been dissolved. */
boolean isDissolved(GroupId g) throws DbException;
/** Stores (and sends) a local group message. */
GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException;
@@ -49,13 +58,32 @@ public interface PrivateGroupManager extends MessageTracker {
/** Returns all private groups the user is a member of. */
Collection<PrivateGroup> getPrivateGroups() throws DbException;
/** Returns true if the private group has been dissolved. */
boolean isDissolved(GroupId g) throws DbException;
/** Returns the body of the group message with the given ID. */
String getMessageBody(MessageId m) throws DbException;
/** Returns the headers of all group messages in the given group. */
Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException;
/** Returns all members of the group with ID g */
Collection<GroupMember> getMembers(GroupId g) throws DbException;
/** Returns true if the given Author a is member of the group with ID g */
boolean isMember(Transaction txn, GroupId g, Author a) throws DbException;
/**
* Registers a hook to be called when members are added
* or groups are removed.
* */
void registerPrivateGroupHook(PrivateGroupHook hook);
@NotNullByDefault
interface PrivateGroupHook {
void addingMember(Transaction txn, GroupId g, Author a)
throws DbException;
void removingGroup(Transaction txn, GroupId g) throws DbException;
}
}

View File

@@ -15,4 +15,7 @@ interface Constants {
String KEY_MEMBER_NAME = "memberName";
String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey";
String KEY_MEMBERS = "members";
String KEY_DISSOLVED = "dissolved";
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.privategroup;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
@@ -13,6 +14,8 @@ import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.privategroup.GroupMember;
import org.briarproject.api.privategroup.GroupMessage;
import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.privategroup.JoinMessageHeader;
@@ -33,17 +36,23 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
import javax.inject.Inject;
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.NEW_MEMBER;
import static org.briarproject.api.privategroup.MessageType.POST;
import static org.briarproject.privategroup.Constants.KEY_DISSOLVED;
import static org.briarproject.privategroup.Constants.KEY_MEMBERS;
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_PUBLIC_KEY;
@@ -54,6 +63,7 @@ import static org.briarproject.privategroup.Constants.KEY_READ;
import static org.briarproject.privategroup.Constants.KEY_TIMESTAMP;
import static org.briarproject.privategroup.Constants.KEY_TYPE;
@NotNullByDefault
public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
PrivateGroupManager {
@@ -65,6 +75,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
private final PrivateGroupFactory privateGroupFactory;
private final IdentityManager identityManager;
private final List<PrivateGroupHook> hooks;
@Inject
PrivateGroupManagerImpl(ClientHelper clientHelper,
@@ -75,6 +86,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
this.privateGroupFactory = privateGroupFactory;
this.identityManager = identityManager;
hooks = new CopyOnWriteArrayList<PrivateGroupHook>();
}
@Override
@@ -89,6 +101,11 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
Transaction txn = db.startTransaction(false);
try {
db.addGroup(txn, group.getGroup());
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_MEMBERS, new BdfList()),
new BdfEntry(KEY_DISSOLVED, false)
);
clientHelper.mergeGroupMetadata(txn, group.getId(), meta);
announceNewMember(txn, newMemberMsg);
joinPrivateGroup(txn, joinMsg);
txn.setComplete();
@@ -114,6 +131,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
addMessageMetadata(meta, m, true);
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
trackOutgoingMessage(txn, m.getMessage());
addMember(txn, m.getMessage().getGroupId(), m.getMember());
setPreviousMsgId(txn, m.getMessage().getGroupId(),
m.getMessage().getId());
}
@@ -121,6 +139,15 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override
public void removePrivateGroup(GroupId g) throws DbException {
// TODO
Transaction txn = db.startTransaction(false);
try {
for (PrivateGroupHook hook : hooks) {
hook.removingGroup(txn, g);
}
txn.setComplete();
} finally {
db.endTransaction(txn);
}
}
@Override
@@ -152,6 +179,19 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
clientHelper.mergeGroupMetadata(txn, g, d);
}
@Override
public void markGroupDissolved(Transaction txn, GroupId g)
throws DbException {
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_DISSOLVED, true)
);
try {
clientHelper.mergeGroupMetadata(txn, g, meta);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public long getMessageTimestamp(MessageId id) throws DbException {
try {
@@ -243,7 +283,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override
public boolean isDissolved(GroupId g) throws DbException {
return false;
try {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(g);
return meta.getBoolean(KEY_DISSOLVED);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
@@ -306,14 +351,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
}
long timestamp = meta.getLong(KEY_TIMESTAMP);
AuthorId authorId = new AuthorId(meta.getRaw(KEY_MEMBER_ID));
String name = meta.getString(KEY_MEMBER_NAME);
byte[] publicKey = meta.getRaw(KEY_MEMBER_PUBLIC_KEY);
Author author = new Author(authorId, name, publicKey);
Author author = getAuthor(meta);
Status status;
if (statuses.containsKey(authorId)) {
status = statuses.get(authorId);
if (statuses.containsKey(author.getId())) {
status = statuses.get(author.getId());
} else {
status = identityManager.getAuthorStatus(txn, author.getId());
}
@@ -327,6 +368,63 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
status, read);
}
@Override
public Collection<GroupMember> getMembers(GroupId g) throws DbException {
Transaction txn = db.startTransaction(true);
try {
Collection<GroupMember> members = new ArrayList<GroupMember>();
Collection<Author> authors = getMembers(txn, g);
for (Author a : authors) {
Status status = identityManager.getAuthorStatus(txn, a.getId());
boolean shared = false;
if (status == VERIFIED || status == UNVERIFIED) {
Collection<ContactId> contacts =
db.getContacts(txn, a.getId());
if (contacts.size() != 1) throw new DbException();
ContactId c = contacts.iterator().next();
shared = db.isVisibleToContact(txn, c, g);
}
members.add(new GroupMember(a, status, shared));
}
txn.setComplete();
return members;
} finally {
db.endTransaction(txn);
}
}
private Collection<Author> getMembers(Transaction txn, GroupId g)
throws DbException {
try {
Collection<Author> members = new ArrayList<Author>();
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList list = meta.getList(KEY_MEMBERS);
for (Object o : list) {
BdfDictionary d = (BdfDictionary) o;
Author member = getAuthor(d);
members.add(member);
}
return members;
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public boolean isMember(Transaction txn, GroupId g, Author a)
throws DbException {
for (Author member : getMembers(txn, g)) {
if (member.equals(a)) return true;
}
return false;
}
@Override
public void registerPrivateGroupHook(PrivateGroupHook hook) {
hooks.add(hook);
}
@Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException {
@@ -365,7 +463,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
db.deleteMessage(txn, m.getId());
return false;
}
// TODO add to member list
addMember(txn, m.getGroupId(), getAuthor(meta));
trackIncomingMessage(txn, m);
return true;
case POST:
@@ -421,4 +519,24 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
}
}
private void addMember(Transaction txn, GroupId g, Author a)
throws DbException, FormatException {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, g);
BdfList members = meta.getList(KEY_MEMBERS);
members.add(BdfDictionary.of(
new BdfEntry(KEY_MEMBER_ID, a.getId()),
new BdfEntry(KEY_MEMBER_NAME, a.getName()),
new BdfEntry(KEY_MEMBER_PUBLIC_KEY, a.getPublicKey())
));
clientHelper.mergeGroupMetadata(txn, g, meta);
}
private Author getAuthor(BdfDictionary meta) throws FormatException {
AuthorId authorId = new AuthorId(meta.getRaw(KEY_MEMBER_ID));
String name = meta.getString(KEY_MEMBER_NAME);
byte[] publicKey = meta.getRaw(KEY_MEMBER_PUBLIC_KEY);
return new Author(authorId, name, publicKey);
}
}