Merge branch '494-implement-backend-for-reblogging-and-blog-comments' into 'master'

Add support for comments and reblogging to Blog Client

Comments and reblogs need to depend on the post they refer to.
Since message dependencies are limited to one group,
the post and also the comments need to be wrapped
when commented on or reblogged to another blog (and group).

For this reason, in addition to comments, two new wrapping message types
are introduced. They retain all data of the original messages and allow
for reconstruction and signature verification.

This MR breaks backwards compatibility with old blog posts.
It removes the content type, title and parent ID from the post.

Furthermore, it includes one commit that replaces the `Message` in `MessageSharedEvent` with a `MessageId`.

Closes #494

See merge request !285
This commit is contained in:
akwizgran
2016-08-30 23:09:31 +00:00
33 changed files with 915 additions and 345 deletions

View File

@@ -5,6 +5,7 @@ import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
@@ -33,17 +34,20 @@ import org.junit.Test;
import java.util.Collection;
import java.util.Collections;
import javax.inject.Inject;
import static org.briarproject.TestUtils.getRandomBytes;
import static org.briarproject.TestUtils.getRandomId;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID;
@@ -68,9 +72,13 @@ public class BlogManagerImplTest extends BriarTestCase {
private final Message message;
private final MessageId messageId;
@Inject
@SuppressWarnings("WeakerAccess")
BlogPostFactory blogPostFactory;
public BlogManagerImplTest() {
blogManager = new BlogManagerImpl(db, identityManager, clientHelper,
metadataParser, contactManager, blogFactory);
metadataParser, contactManager, blogFactory, blogPostFactory);
blog1 = getBlog("Test Blog 1", "Test Description 1");
blog2 = getBlog("Test Blog 2", "Test Description 2");
@@ -183,16 +191,15 @@ public class BlogManagerImplTest extends BriarTestCase {
BdfList list = new BdfList();
BdfDictionary author = authorToBdfDictionary(blog1.getAuthor());
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_TYPE, POST.getInt()),
new BdfEntry(KEY_TIMESTAMP, 0),
new BdfEntry(KEY_TIME_RECEIVED, 1),
new BdfEntry(KEY_AUTHOR, author),
new BdfEntry(KEY_CONTENT_TYPE, 0),
new BdfEntry(KEY_READ, false),
new BdfEntry(KEY_CONTENT_TYPE, "text/plain")
new BdfEntry(KEY_READ, false)
);
context.checking(new Expectations() {{
oneOf(clientHelper).setMessageShared(txn, message, true);
oneOf(clientHelper).setMessageShared(txn, messageId, true);
oneOf(identityManager)
.getAuthorStatus(txn, blog1.getAuthor().getId());
will(returnValue(VERIFIED));
@@ -211,7 +218,6 @@ public class BlogManagerImplTest extends BriarTestCase {
assertEquals(messageId, h.getId());
assertEquals(null, h.getParentId());
assertEquals(VERIFIED, h.getAuthorStatus());
assertEquals("text/plain", h.getContentType());
assertEquals(blog1.getAuthor(), h.getAuthor());
}
@@ -273,13 +279,12 @@ public class BlogManagerImplTest extends BriarTestCase {
public void testAddLocalPost() throws DbException, FormatException {
final Transaction txn = new Transaction(null, true);
final BlogPost post =
new BlogPost(null, message, null, blog1.getAuthor(),
"text/plain");
new BlogPost(message, null, blog1.getAuthor());
BdfDictionary authorMeta = authorToBdfDictionary(blog1.getAuthor());
final BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_TYPE, POST.getInt()),
new BdfEntry(KEY_TIMESTAMP, message.getTimestamp()),
new BdfEntry(KEY_AUTHOR, authorMeta),
new BdfEntry(KEY_CONTENT_TYPE, "text/plain"),
new BdfEntry(KEY_READ, true)
);
@@ -308,7 +313,6 @@ public class BlogManagerImplTest extends BriarTestCase {
assertEquals(messageId, h.getId());
assertEquals(null, h.getParentId());
assertEquals(VERIFIED, h.getAuthorStatus());
assertEquals("text/plain", h.getContentType());
assertEquals(blog1.getAuthor(), h.getAuthor());
}

View File

@@ -37,12 +37,10 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
import static org.briarproject.api.blogs.MessageType.COMMENT;
import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
@@ -70,8 +68,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
private final BlogFactory blogFactory = context.mock(BlogFactory.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final Clock clock = new SystemClock();
private final byte[] body = TestUtils.getRandomBytes(
MAX_BLOG_POST_BODY_LENGTH);
private final String body = TestUtils.getRandomString(42);
private final String contentType = "text/plain";
public BlogPostValidatorTest() {
@@ -105,18 +102,15 @@ public class BlogPostValidatorTest extends BriarTestCase {
@Test
public void testValidateProperBlogPost()
throws IOException, GeneralSecurityException {
// content type, title (optional), post body, attachments
BdfList content = BdfList.of(null, contentType, null, body, null);
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
BdfList m = BdfList.of(POST.getInt(), body, sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), content);
BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(signed, sigBytes, true);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertFalse(result.getBoolean(KEY_READ));
context.assertIsSatisfied();
@@ -143,13 +137,11 @@ public class BlogPostValidatorTest extends BriarTestCase {
@Test(expected = InvalidMessageException.class)
public void testValidateBlogPostWithBadSignature()
throws IOException, GeneralSecurityException {
// content type, title (optional), post body, attachments
BdfList content = BdfList.of(null, contentType, null, body, null);
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
BdfList m = BdfList.of(POST.getInt(), body, sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), content);
BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(signed, sigBytes, false);
validator.validateMessage(message, group, m).getDictionary();
}
@@ -157,17 +149,18 @@ public class BlogPostValidatorTest extends BriarTestCase {
@Test
public void testValidateProperBlogComment()
throws IOException, GeneralSecurityException {
// comment, parent_original_id, signature, parent_current_id
// comment, parent_original_id, parent_id, signature
String comment = "This is a blog comment";
MessageId originalId = new MessageId(TestUtils.getRandomId());
byte[] currentId = TestUtils.getRandomId();
MessageId currentId = new MessageId(TestUtils.getRandomId());
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(COMMENT.getInt(), comment, originalId,
sigBytes, currentId);
BdfList m =
BdfList.of(COMMENT.getInt(), comment, originalId, currentId,
sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), comment,
originalId);
originalId, currentId);
expectCrypto(signed, sigBytes, true);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
@@ -175,7 +168,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
assertEquals(comment, result.getString(KEY_COMMENT));
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertEquals(originalId.getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
assertEquals(currentId, result.getRaw(KEY_CURRENT_MSG_ID));
assertEquals(currentId.getBytes(), result.getRaw(KEY_PARENT_MSG_ID));
assertFalse(result.getBoolean(KEY_READ));
context.assertIsSatisfied();
}
@@ -185,14 +178,15 @@ public class BlogPostValidatorTest extends BriarTestCase {
throws IOException, GeneralSecurityException {
// comment, parent_original_id, signature, parent_current_id
MessageId originalId = new MessageId(TestUtils.getRandomId());
byte[] currentId = TestUtils.getRandomId();
MessageId currentId = new MessageId(TestUtils.getRandomId());
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(COMMENT.getInt(), null, originalId, sigBytes,
currentId);
BdfList m =
BdfList.of(COMMENT.getInt(), null, originalId, currentId,
sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), null,
originalId);
originalId, currentId);
expectCrypto(signed, sigBytes, true);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
@@ -205,17 +199,16 @@ public class BlogPostValidatorTest extends BriarTestCase {
public void testValidateProperWrappedPost()
throws IOException, GeneralSecurityException {
// group descriptor, timestamp, content, signature
BdfList content = BdfList.of(null, contentType, null, body, null);
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m =
BdfList.of(WRAPPED_POST.getInt(), descriptor,
message.getTimestamp(), content, sigBytes);
message.getTimestamp(), body, sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), content);
BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(signed, sigBytes, true);
final BdfList originalList = BdfList.of(content, sigBytes);
final BdfList originalList = BdfList.of(POST.getInt(), body, sigBytes);
final byte[] originalBody = TestUtils.getRandomBytes(42);
context.checking(new Expectations() {{
@@ -232,7 +225,6 @@ public class BlogPostValidatorTest extends BriarTestCase {
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
context.assertIsSatisfied();
}
@@ -244,18 +236,19 @@ public class BlogPostValidatorTest extends BriarTestCase {
// parent_current_id
String comment = "This is another comment";
MessageId originalId = new MessageId(TestUtils.getRandomId());
MessageId oldId = new MessageId(TestUtils.getRandomId());
final byte[] sigBytes = TestUtils.getRandomBytes(42);
MessageId currentId = new MessageId(TestUtils.getRandomId());
BdfList m = BdfList.of(WRAPPED_COMMENT.getInt(), descriptor,
message.getTimestamp(), comment, originalId, sigBytes,
message.getTimestamp(), comment, originalId, oldId, sigBytes,
currentId);
BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
comment, originalId);
comment, originalId, oldId);
expectCrypto(signed, sigBytes, true);
final BdfList originalList = BdfList.of(comment, originalId, sigBytes,
currentId);
final BdfList originalList = BdfList.of(COMMENT.getInt(), comment,
originalId, oldId, sigBytes);
final byte[] originalBody = TestUtils.getRandomBytes(42);
context.checking(new Expectations() {{
@@ -276,7 +269,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertEquals(
message.getId().getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
assertEquals(currentId.getBytes(), result.getRaw(KEY_CURRENT_MSG_ID));
assertEquals(currentId.getBytes(), result.getRaw(KEY_PARENT_MSG_ID));
context.assertIsSatisfied();
}

View File

@@ -751,7 +751,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transaction = db.startTransaction(false);
try {
db.setMessageShared(transaction, message, true);
db.setMessageShared(transaction, message.getId(), true);
fail();
} catch (NoSuchMessageException expected) {
// Expected