Skip to content
Snippets Groups Projects
Verified Commit cb61d910 authored by Torsten Grote's avatar Torsten Grote
Browse files

Add methods and hooks to PrivateGroupManager related to members and removal

parent 399a4890
No related branches found
No related tags found
1 merge request!377Add methods and hooks to PrivateGroupManager related to members and removal
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;
}
}
...@@ -3,12 +3,15 @@ package org.briarproject.api.privategroup; ...@@ -3,12 +3,15 @@ package org.briarproject.api.privategroup;
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.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;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import java.util.Collection; import java.util.Collection;
@NotNullByDefault
public interface PrivateGroupManager extends MessageTracker { public interface PrivateGroupManager extends MessageTracker {
/** Returns the unique ID of the private group client. */ /** Returns the unique ID of the private group client. */
...@@ -35,6 +38,12 @@ public interface PrivateGroupManager extends MessageTracker { ...@@ -35,6 +38,12 @@ public interface PrivateGroupManager extends MessageTracker {
// TODO change to getPreviousMessageHeader() // TODO change to getPreviousMessageHeader()
long getMessageTimestamp(MessageId id) throws DbException; 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. */ /** Stores (and sends) a local group message. */
GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException; GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException;
...@@ -49,13 +58,32 @@ public interface PrivateGroupManager extends MessageTracker { ...@@ -49,13 +58,32 @@ public interface PrivateGroupManager extends MessageTracker {
/** Returns all private groups the user is a member of. */ /** Returns all private groups the user is a member of. */
Collection<PrivateGroup> getPrivateGroups() throws DbException; 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. */ /** Returns the body of the group message with the given ID. */
String getMessageBody(MessageId m) throws DbException; String getMessageBody(MessageId m) throws DbException;
/** Returns the headers of all group messages in the given group. */ /** Returns the headers of all group messages in the given group. */
Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException; 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;
}
} }
...@@ -15,4 +15,7 @@ interface Constants { ...@@ -15,4 +15,7 @@ interface Constants {
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 KEY_DISSOLVED = "dissolved";
} }
...@@ -2,6 +2,7 @@ package org.briarproject.privategroup; ...@@ -2,6 +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.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;
...@@ -13,6 +14,8 @@ import org.briarproject.api.identity.Author; ...@@ -13,6 +14,8 @@ import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status; import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager; 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.GroupMessage;
import org.briarproject.api.privategroup.GroupMessageHeader; import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.privategroup.JoinMessageHeader; import org.briarproject.api.privategroup.JoinMessageHeader;
...@@ -33,17 +36,23 @@ import java.util.Arrays; ...@@ -33,17 +36,23 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger; import java.util.logging.Logger;
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.NEW_MEMBER; import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER;
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.privategroup.Constants.KEY_MEMBERS;
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;
...@@ -54,6 +63,7 @@ import static org.briarproject.privategroup.Constants.KEY_READ; ...@@ -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_TIMESTAMP;
import static org.briarproject.privategroup.Constants.KEY_TYPE; import static org.briarproject.privategroup.Constants.KEY_TYPE;
@NotNullByDefault
public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
PrivateGroupManager { PrivateGroupManager {
...@@ -65,6 +75,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -65,6 +75,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final List<PrivateGroupHook> hooks;
@Inject @Inject
PrivateGroupManagerImpl(ClientHelper clientHelper, PrivateGroupManagerImpl(ClientHelper clientHelper,
...@@ -75,6 +86,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -75,6 +86,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.identityManager = identityManager; this.identityManager = identityManager;
hooks = new CopyOnWriteArrayList<PrivateGroupHook>();
} }
@Override @Override
...@@ -89,6 +101,11 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -89,6 +101,11 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
db.addGroup(txn, group.getGroup()); 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); announceNewMember(txn, newMemberMsg);
joinPrivateGroup(txn, joinMsg); joinPrivateGroup(txn, joinMsg);
txn.setComplete(); txn.setComplete();
...@@ -114,6 +131,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -114,6 +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());
setPreviousMsgId(txn, m.getMessage().getGroupId(), setPreviousMsgId(txn, m.getMessage().getGroupId(),
m.getMessage().getId()); m.getMessage().getId());
} }
...@@ -121,6 +139,15 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -121,6 +139,15 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override @Override
public void removePrivateGroup(GroupId g) throws DbException { public void removePrivateGroup(GroupId g) throws DbException {
// TODO // TODO
Transaction txn = db.startTransaction(false);
try {
for (PrivateGroupHook hook : hooks) {
hook.removingGroup(txn, g);
}
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} }
@Override @Override
...@@ -152,6 +179,19 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -152,6 +179,19 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
clientHelper.mergeGroupMetadata(txn, g, d); 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 @Override
public long getMessageTimestamp(MessageId id) throws DbException { public long getMessageTimestamp(MessageId id) throws DbException {
try { try {
...@@ -243,7 +283,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -243,7 +283,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override @Override
public boolean isDissolved(GroupId g) throws DbException { 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 @Override
...@@ -306,14 +351,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -306,14 +351,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
} }
long timestamp = meta.getLong(KEY_TIMESTAMP); long timestamp = meta.getLong(KEY_TIMESTAMP);
AuthorId authorId = new AuthorId(meta.getRaw(KEY_MEMBER_ID)); Author author = getAuthor(meta);
String name = meta.getString(KEY_MEMBER_NAME);
byte[] publicKey = meta.getRaw(KEY_MEMBER_PUBLIC_KEY);
Author author = new Author(authorId, name, publicKey);
Status status; Status status;
if (statuses.containsKey(authorId)) { if (statuses.containsKey(author.getId())) {
status = statuses.get(authorId); status = statuses.get(author.getId());
} else { } else {
status = identityManager.getAuthorStatus(txn, author.getId()); status = identityManager.getAuthorStatus(txn, author.getId());
} }
...@@ -327,6 +368,63 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -327,6 +368,63 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
status, read); 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 @Override
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 {
...@@ -365,7 +463,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -365,7 +463,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
db.deleteMessage(txn, m.getId()); db.deleteMessage(txn, m.getId());
return false; return false;
} }
// TODO add to member list addMember(txn, m.getGroupId(), getAuthor(meta));
trackIncomingMessage(txn, m); trackIncomingMessage(txn, m);
return true; return true;
case POST: case POST:
...@@ -421,4 +519,24 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements ...@@ -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);
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment