Remove forum content type and move bodies to string

Also removes support for anonymous forum posts.
Closes #698
This commit is contained in:
Torsten Grote
2016-10-27 11:24:33 -02:00
parent c0aa255bb6
commit 5ce8b1978d
10 changed files with 81 additions and 124 deletions

View File

@@ -7,6 +7,8 @@ import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
@@ -34,7 +36,7 @@ import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule; import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import org.briarproject.util.StringUtils; import org.jetbrains.annotations.Nullable;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -53,7 +55,6 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.briarproject.TestPluginsModule.MAX_LATENCY; import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
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;
import static org.briarproject.api.sync.ValidationManager.State.PENDING; import static org.briarproject.api.sync.ValidationManager.State.PENDING;
@@ -75,6 +76,8 @@ public class ForumManagerTest extends BriarIntegrationTest {
@Inject @Inject
AuthorFactory authorFactory; AuthorFactory authorFactory;
@Inject @Inject
CryptoComponent crypto;
@Inject
ForumPostFactory forumPostFactory; ForumPostFactory forumPostFactory;
// objects accessed from background threads need to be volatile // objects accessed from background threads need to be volatile
@@ -127,16 +130,17 @@ public class ForumManagerTest extends BriarIntegrationTest {
deliveryWaiter = new Waiter(); deliveryWaiter = new Waiter();
} }
private ForumPost createForumPost(GroupId groupId, ForumPost parent, private ForumPost createForumPost(GroupId groupId,
String body, long ms) throws Exception { @Nullable ForumPost parent, String body, long ms) throws Exception {
return forumPostFactory.createAnonymousPost(groupId, ms, return forumPostFactory.createPost(groupId, ms,
parent == null ? null : parent.getMessage().getId(), parent == null ? null : parent.getMessage().getId(),
"text/plain", StringUtils.toUtf8(body)); author0, body);
} }
@Test @Test
public void testForumPost() throws Exception { public void testForumPost() throws Exception {
startLifecycles(); startLifecycles();
addDefaultIdentities();
Forum forum = forumManager0.addForum("TestForum"); Forum forum = forumManager0.addForum("TestForum");
assertEquals(1, forumManager0.getForums().size()); assertEquals(1, forumManager0.getForums().size());
final long ms1 = clock.currentTimeMillis() - 1000L; final long ms1 = clock.currentTimeMillis() - 1000L;
@@ -167,8 +171,7 @@ public class ForumManagerTest extends BriarIntegrationTest {
forumManager0.getPostHeaders(forum.getGroup().getId()); forumManager0.getPostHeaders(forum.getGroup().getId());
assertEquals(2, headers.size()); assertEquals(2, headers.size());
for (ForumPostHeader h : headers) { for (ForumPostHeader h : headers) {
final String hBody = final String hBody = forumManager0.getPostBody(h.getId());
StringUtils.fromUtf8(forumManager0.getPostBody(h.getId()));
boolean isPost1 = h.getId().equals(post1.getMessage().getId()); boolean isPost1 = h.getId().equals(post1.getMessage().getId());
boolean isPost2 = h.getId().equals(post2.getMessage().getId()); boolean isPost2 = h.getId().equals(post2.getMessage().getId());
@@ -366,13 +369,18 @@ public class ForumManagerTest extends BriarIntegrationTest {
} }
private void addDefaultIdentities() throws DbException { private void addDefaultIdentities() throws DbException {
author0 = authorFactory.createLocalAuthor(SHARER, KeyPair keyPair0 = crypto.generateSignatureKeyPair();
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH), byte[] publicKey0 = keyPair0.getPublic().getEncoded();
TestUtils.getRandomBytes(123)); byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
author0 = authorFactory
.createLocalAuthor(SHARER, publicKey0, privateKey0);
identityManager0.addLocalAuthor(author0); identityManager0.addLocalAuthor(author0);
author1 = authorFactory.createLocalAuthor(INVITEE,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH), KeyPair keyPair1 = crypto.generateSignatureKeyPair();
TestUtils.getRandomBytes(123)); byte[] publicKey1 = keyPair1.getPublic().getEncoded();
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
author1 = authorFactory
.createLocalAuthor(INVITEE, publicKey1, privateKey1);
identityManager1.addLocalAuthor(author1); identityManager1.addLocalAuthor(author1);
} }

View File

@@ -820,7 +820,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
long time = clock.currentTimeMillis(); long time = clock.currentTimeMillis();
String body = TestUtils.getRandomString(42); String body = TestUtils.getRandomString(42);
ForumPost p = forumPostFactory ForumPost p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author0, .createPost(forum0.getId(), time, null, author0,
body); body);
forumManager0.addLocalPost(p); forumManager0.addLocalPost(p);
@@ -839,7 +839,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
time = clock.currentTimeMillis(); time = clock.currentTimeMillis();
body = TestUtils.getRandomString(42); body = TestUtils.getRandomString(42);
p = forumPostFactory p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author1, .createPost(forum0.getId(), time, null, author1,
body); body);
forumManager1.addLocalPost(p); forumManager1.addLocalPost(p);
@@ -883,7 +883,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
time = clock.currentTimeMillis(); time = clock.currentTimeMillis();
body = TestUtils.getRandomString(42); body = TestUtils.getRandomString(42);
p = forumPostFactory p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author1, .createPost(forum0.getId(), time, null, author1,
body); body);
forumManager1.addLocalPost(p); forumManager1.addLocalPost(p);

View File

@@ -77,7 +77,7 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
long timestamp = Long.MAX_VALUE; long timestamp = Long.MAX_VALUE;
MessageId parent = new MessageId(TestUtils.getRandomId()); MessageId parent = new MessageId(TestUtils.getRandomId());
String body = TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH); String body = TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH);
ForumPost post = forumPostFactory.createPseudonymousPost(groupId, ForumPost post = forumPostFactory.createPost(groupId,
timestamp, parent, author, body); timestamp, parent, author, body);
// Check the size of the serialised message // Check the size of the serialised message
int length = post.getMessage().getRaw().length; int length = post.getMessage().getRaw().length;

View File

@@ -21,7 +21,6 @@ import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.util.StringUtils;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -89,7 +88,7 @@ public class ForumControllerImpl
@Override @Override
protected String loadMessageBody(ForumPostHeader h) throws DbException { protected String loadMessageBody(ForumPostHeader h) throws DbException {
return StringUtils.fromUtf8(forumManager.getPostBody(h.getId())); return forumManager.getPostBody(h.getId());
} }
@Override @Override

View File

@@ -41,7 +41,7 @@ public interface ForumManager extends MessageTracker {
Collection<Forum> getForums() throws DbException; Collection<Forum> getForums() throws DbException;
/** Returns the body of the forum post with the given ID. */ /** Returns the body of the forum post with the given ID. */
byte[] getPostBody(MessageId m) throws DbException; String getPostBody(MessageId m) throws DbException;
/** Returns the headers of all posts in the given forum. */ /** Returns the headers of all posts in the given forum. */
Collection<ForumPostHeader> getPostHeaders(GroupId g) throws DbException; Collection<ForumPostHeader> getPostHeaders(GroupId g) throws DbException;

View File

@@ -1,6 +1,7 @@
package org.briarproject.api.forum; package org.briarproject.api.forum;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -9,11 +10,9 @@ import java.security.GeneralSecurityException;
public interface ForumPostFactory { public interface ForumPostFactory {
ForumPost createAnonymousPost(GroupId groupId, long timestamp, @CryptoExecutor
MessageId parent, String contentType, byte[] body) ForumPost createPost(GroupId groupId, long timestamp, MessageId parent,
throws FormatException; LocalAuthor author, String body)
ForumPost createPseudonymousPost(GroupId groupId, long timestamp,
MessageId parent, LocalAuthor author, String body)
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
} }

View File

@@ -131,7 +131,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
ForumPost p; ForumPost p;
try { try {
p = forumPostFactory p = forumPostFactory
.createPseudonymousPost(groupId, timestamp, parentId, .createPost(groupId, timestamp, parentId,
author, body); author, body);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -213,11 +213,11 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
} }
@Override @Override
public byte[] getPostBody(MessageId m) throws DbException { public String getPostBody(MessageId m) throws DbException {
try { try {
// Parent ID, author, content type, forum post body, signature // Parent ID, author, forum post body, signature
BdfList message = clientHelper.getMessageAsList(m); BdfList message = clientHelper.getMessageAsList(m);
return message.getRaw(3); return message.getString(2);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }

View File

@@ -40,9 +40,9 @@ public class ForumModule {
} }
@Provides @Provides
ForumPostFactory provideForumPostFactory(CryptoComponent crypto, ForumPostFactory provideForumPostFactory(
ClientHelper clientHelper) { ForumPostFactoryImpl forumPostFactory) {
return new ForumPostFactoryImpl(crypto, clientHelper); return forumPostFactory;
} }
@Provides @Provides

View File

@@ -2,10 +2,6 @@ package org.briarproject.forum;
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.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.PrivateKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.forum.ForumPost; import org.briarproject.api.forum.ForumPost;
import org.briarproject.api.forum.ForumPostFactory; import org.briarproject.api.forum.ForumPostFactory;
@@ -19,64 +15,35 @@ import java.security.GeneralSecurityException;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
class ForumPostFactoryImpl implements ForumPostFactory { class ForumPostFactoryImpl implements ForumPostFactory {
private final CryptoComponent crypto;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
@Inject @Inject
ForumPostFactoryImpl(CryptoComponent crypto, ClientHelper clientHelper) { ForumPostFactoryImpl(ClientHelper clientHelper) {
this.crypto = crypto;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
} }
@Override @Override
public ForumPost createAnonymousPost(GroupId groupId, long timestamp, public ForumPost createPost(GroupId groupId, long timestamp,
MessageId parent, String contentType, byte[] body) MessageId parent, LocalAuthor author, String body)
throws FormatException {
// Validate the arguments
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
throw new IllegalArgumentException();
if (body.length > MAX_FORUM_POST_BODY_LENGTH)
throw new IllegalArgumentException();
// Serialise the message
BdfList message = BdfList.of(parent, null, contentType, body, null);
Message m = clientHelper.createMessage(groupId, timestamp, message);
return new ForumPost(m, parent, null);
}
@Override
public ForumPost createPseudonymousPost(GroupId groupId, long timestamp,
MessageId parent, LocalAuthor author, String bodyStr)
throws FormatException, GeneralSecurityException { throws FormatException, GeneralSecurityException {
String contentType = "text/plain";
byte[] body = StringUtils.toUtf8(bodyStr);
// Validate the arguments // Validate the arguments
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH) if (StringUtils.isTooLong(body, MAX_FORUM_POST_BODY_LENGTH))
throw new IllegalArgumentException();
if (body.length > MAX_FORUM_POST_BODY_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// Serialise the data to be signed // Serialise the data to be signed
BdfList authorList = BdfList.of(author.getName(), BdfList authorList =
author.getPublicKey()); BdfList.of(author.getName(), author.getPublicKey());
BdfList signed = BdfList.of(groupId, timestamp, parent, authorList, BdfList signed = BdfList.of(groupId, timestamp, parent, authorList,
contentType, body); body);
// Get private key // Sign the data
KeyParser keyParser = crypto.getSignatureKeyParser(); byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
byte[] k = author.getPrivateKey();
PrivateKey privateKey = keyParser.parsePrivateKey(k);
// Generate the signature
Signature signature = crypto.getSignature();
signature.initSign(privateKey);
signature.update(clientHelper.toByteArray(signed));
byte[] sig = signature.sign();
// Serialise the signed message // Serialise the signed message
BdfList message = BdfList.of(parent, authorList, contentType, body, BdfList message = BdfList.of(parent, authorList, body, sig);
sig);
Message m = clientHelper.createMessage(groupId, timestamp, message); Message m = clientHelper.createMessage(groupId, timestamp, message);
return new ForumPost(m, parent, author); return new ForumPost(m, parent, author);
} }
} }

View File

@@ -20,7 +20,6 @@ import java.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -40,50 +39,38 @@ class ForumPostValidator extends BdfMessageValidator {
protected BdfMessageContext validateMessage(Message m, Group g, protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws InvalidMessageException, FormatException { BdfList body) throws InvalidMessageException, FormatException {
// Parent ID, author, content type, forum post body, signature // Parent ID, author, content type, forum post body, signature
checkSize(body, 5); checkSize(body, 4);
// Parent ID is optional // Parent ID is optional
byte[] parent = body.getOptionalRaw(0); byte[] parent = body.getOptionalRaw(0);
checkLength(parent, UniqueId.LENGTH); checkLength(parent, UniqueId.LENGTH);
// Author is optional
Author author = null; // Author
BdfList authorList = body.getOptionalList(1); BdfList authorList = body.getList(1);
if (authorList != null) { // Name, public key
// Name, public key checkSize(authorList, 2);
checkSize(authorList, 2); String name = authorList.getString(0);
String name = authorList.getString(0); checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH); byte[] publicKey = authorList.getRaw(1);
byte[] publicKey = authorList.getRaw(1); checkLength(publicKey, 0, MAX_PUBLIC_KEY_LENGTH);
checkLength(publicKey, 0, MAX_PUBLIC_KEY_LENGTH); Author author = authorFactory.createAuthor(name, publicKey);
author = authorFactory.createAuthor(name, publicKey);
}
// Content type
String contentType = body.getString(2);
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
// Forum post body // Forum post body
byte[] forumPostBody = body.getRaw(3); String forumPostBody = body.getString(2);
checkLength(forumPostBody, 0, MAX_FORUM_POST_BODY_LENGTH); checkLength(forumPostBody, 0, MAX_FORUM_POST_BODY_LENGTH);
// Signature is optional
byte[] sig = body.getOptionalRaw(4); // Signature
byte[] sig = body.getRaw(3);
checkLength(sig, 0, MAX_SIGNATURE_LENGTH); checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
// If there's an author there must be a signature and vice versa // Verify the signature
if (author != null && sig == null) { BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
throw new InvalidMessageException("Author without signature"); authorList, forumPostBody);
} try {
if (author == null && sig != null) { clientHelper.verifySignature(sig, author.getPublicKey(), signed);
throw new InvalidMessageException("Signature without author"); } catch (GeneralSecurityException e) {
} throw new InvalidMessageException(e);
// Verify the signature, if any
if (author != null) {
// Serialise the data to be verified
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
authorList, contentType, forumPostBody);
try {
clientHelper
.verifySignature(sig, author.getPublicKey(), signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
} }
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
Collection<MessageId> dependencies = Collections.emptyList(); Collection<MessageId> dependencies = Collections.emptyList();
@@ -92,14 +79,11 @@ class ForumPostValidator extends BdfMessageValidator {
meta.put("parent", parent); meta.put("parent", parent);
dependencies = Collections.singletonList(new MessageId(parent)); dependencies = Collections.singletonList(new MessageId(parent));
} }
if (author != null) { BdfDictionary authorMeta = new BdfDictionary();
BdfDictionary authorMeta = new BdfDictionary(); authorMeta.put("id", author.getId());
authorMeta.put("id", author.getId()); authorMeta.put("name", author.getName());
authorMeta.put("name", author.getName()); authorMeta.put("publicKey", author.getPublicKey());
authorMeta.put("publicKey", author.getPublicKey()); meta.put("author", authorMeta);
meta.put("author", authorMeta);
}
meta.put("contentType", contentType);
meta.put("read", false); meta.put("read", false);
return new BdfMessageContext(meta, dependencies); return new BdfMessageContext(meta, dependencies);
} }