From 280f3ba1fca79bb9186118350e47dae430c3e970 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 5 Oct 2018 15:17:42 -0300 Subject: [PATCH] briar-headless: POST text as JSON in body instead of form parameter --- briar-headless/README.md | 16 ++++++++++++-- .../org/briarproject/briar/headless/Router.kt | 20 +++++++++++++++++ .../headless/blogs/BlogControllerImpl.kt | 11 +++++----- .../headless/forums/ForumControllerImpl.kt | 9 ++++---- .../messaging/MessagingControllerImpl.kt | 5 ++--- .../headless/blogs/BlogControllerTest.kt | 20 +++++------------ .../headless/forums/ForumControllerTest.kt | 22 +++++++++++++++---- .../messaging/MessagingControllerImplTest.kt | 8 +++---- 8 files changed, 74 insertions(+), 37 deletions(-) diff --git a/briar-headless/README.md b/briar-headless/README.md index 5b4b9feee..5edb4e927 100644 --- a/briar-headless/README.md +++ b/briar-headless/README.md @@ -101,7 +101,13 @@ Attention: There can messages of other `type`s where the message `body` is `null `POST /messages/{contactId}` -The text of the message should be included in the form parameter `text`. +The text of the message should be posted as JSON: + +```json +{ + "text": "Hello World!" +} +``` ### Listing blog posts @@ -132,7 +138,13 @@ Returns a JSON array of blog posts: `POST /v1/blogs/posts` -The text of the blog post should be included in the form parameter `text`. +The text of the blog post should be posted as JSON: + +```json +{ + "text": "Hello Blog World!" +} +``` ## Websocket API diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt index c1d4563cf..5da82e75b 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt @@ -1,5 +1,9 @@ package org.briarproject.briar.headless +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.databind.ObjectMapper +import io.javalin.BadRequestResponse +import io.javalin.Context import io.javalin.Javalin import io.javalin.JavalinEvent.SERVER_START_FAILED import io.javalin.JavalinEvent.SERVER_STOPPED @@ -103,3 +107,19 @@ constructor( } } + +/** + * Returns a String from the JSON field or throws [BadRequestResponse] if null or empty. + */ +fun Context.getFromJson(field: String) : String { + try { + // TODO use a static object mapper to avoid re-initializations + val jsonNode = ObjectMapper().readTree(body()) + if (!jsonNode.hasNonNull(field)) throw BadRequestResponse("'$field' missing in JSON") + val result = jsonNode.get(field).asText() + if (result == null || result.isEmpty()) throw BadRequestResponse("'$field' empty in JSON") + return result + } catch (e: JsonParseException) { + throw BadRequestResponse("Invalid JSON") + } +} diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogControllerImpl.kt index d3f179e48..5f1dc0142 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/blogs/BlogControllerImpl.kt @@ -5,9 +5,10 @@ import io.javalin.Context import org.briarproject.bramble.api.identity.IdentityManager import org.briarproject.bramble.api.system.Clock import org.briarproject.bramble.util.StringUtils -import org.briarproject.briar.api.blog.BlogConstants +import org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH import org.briarproject.briar.api.blog.BlogManager import org.briarproject.briar.api.blog.BlogPostFactory +import org.briarproject.briar.headless.getFromJson import javax.annotation.concurrent.Immutable import javax.inject.Inject import javax.inject.Singleton @@ -26,16 +27,16 @@ constructor( override fun listPosts(ctx: Context): Context { val posts = blogManager.blogs .flatMap { blog -> blogManager.getPostHeaders(blog.id) } + .asSequence() .sortedBy { it.timeReceived } .map { header -> header.output(blogManager.getPostBody(header.id)) } + .toList() return ctx.json(posts) } override fun createPost(ctx: Context): Context { - val text = ctx.formParam("text") - if (text == null || text.isEmpty()) - throw BadRequestResponse("Expecting blog post text") - if (StringUtils.utf8IsTooLong(text, BlogConstants.MAX_BLOG_POST_BODY_LENGTH)) + val text = ctx.getFromJson("text") + if (StringUtils.utf8IsTooLong(text, MAX_BLOG_POST_BODY_LENGTH)) throw BadRequestResponse("Too long blog post text") val author = identityManager.localAuthor diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumControllerImpl.kt index 952f6e8ee..a1e0ad0a1 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/forums/ForumControllerImpl.kt @@ -3,8 +3,9 @@ package org.briarproject.briar.headless.forums import io.javalin.BadRequestResponse import io.javalin.Context import org.briarproject.bramble.util.StringUtils -import org.briarproject.briar.api.forum.ForumConstants +import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH import org.briarproject.briar.api.forum.ForumManager +import org.briarproject.briar.headless.getFromJson import javax.annotation.concurrent.Immutable import javax.inject.Inject import javax.inject.Singleton @@ -20,10 +21,8 @@ constructor(private val forumManager: ForumManager) : ForumController { } override fun create(ctx: Context): Context { - val name = ctx.formParam("text") - if (name == null || name.isNullOrEmpty()) - throw BadRequestResponse("Expecting Forum Name") - if (StringUtils.utf8IsTooLong(name, ForumConstants.MAX_FORUM_NAME_LENGTH)) + val name = ctx.getFromJson("text") + if (StringUtils.utf8IsTooLong(name, MAX_FORUM_NAME_LENGTH)) throw BadRequestResponse("Forum name is too long") return ctx.json(forumManager.addForum(name).output()) } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt index 34fbca73e..6fb20b682 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingControllerImpl.kt @@ -25,6 +25,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse import org.briarproject.briar.headless.event.WebSocketController import org.briarproject.briar.headless.event.output +import org.briarproject.briar.headless.getFromJson import org.briarproject.briar.headless.json.JsonDict import java.util.concurrent.Executor import javax.annotation.concurrent.Immutable @@ -59,9 +60,7 @@ constructor( override fun write(ctx: Context): Context { val contact = getContact(ctx) - val message = ctx.formParam("text") - if (message == null || message.isEmpty()) - throw BadRequestResponse("Expecting Message text") + val message = ctx.getFromJson("text") if (utf8IsTooLong(message, MAX_PRIVATE_MESSAGE_BODY_LENGTH)) throw BadRequestResponse("Message text too large") diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt index 55abc28c4..b7a9d8c38 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt @@ -30,23 +30,15 @@ internal class BlogControllerTest : ControllerTest() { private val rssFeed = false private val read = true private val header = BlogPostHeader( - POST, - group.id, - message.id, - parentId, - message.timestamp, - timestamp, - author, - OURSELVES, - rssFeed, - read + POST, group.id, message.id, parentId, message.timestamp, timestamp, author, OURSELVES, + rssFeed, read ) @Test fun testCreate() { val post = BlogPost(message, null, localAuthor) - every { ctx.formParam("text") } returns body + every { ctx.body() } returns """{"text": "$body"}""" every { identityManager.localAuthor } returns localAuthor every { blogManager.getPersonalBlog(localAuthor) } returns blog every { clock.currentTimeMillis() } returns message.timestamp @@ -68,21 +60,21 @@ internal class BlogControllerTest : ControllerTest() { @Test fun testCreateNoText() { - every { ctx.formParam("text") } returns null + every { ctx.body() } returns """{"foo": "bar"}""" assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) } } @Test fun testCreateEmptyText() { - every { ctx.formParam("text") } returns "" + every { ctx.body() } returns """{"text": ""}""" assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) } } @Test fun testCreateTooLongText() { - every { ctx.formParam("text") } returns getRandomString(MAX_BLOG_POST_BODY_LENGTH + 1) + every { ctx.body() } returns """{"text": "${getRandomString(MAX_BLOG_POST_BODY_LENGTH + 1)}"}""" assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) } } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt index e413e9d96..d239bbcb5 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/forums/ForumControllerTest.kt @@ -30,7 +30,7 @@ internal class ForumControllerTest : ControllerTest() { @Test fun create() { - every { ctx.formParam("text") } returns forum.name + every { ctx.body() } returns """{"text": "${forum.name}"}""" every { forumManager.addForum(forum.name) } returns forum every { ctx.json(forum.output()) } returns ctx @@ -39,21 +39,35 @@ internal class ForumControllerTest : ControllerTest() { @Test fun createNoName() { - every { ctx.formParam("text") } returns null + every { ctx.body() } returns "{}" assertThrows(BadRequestResponse::class.java) { controller.create(ctx) } } @Test fun createEmptyName() { - every { ctx.formParam("text") } returns "" + every { ctx.body() } returns """{"text": ""}""" + + assertThrows(BadRequestResponse::class.java) { controller.create(ctx) } + } + + @Test + fun createNullName() { + every { ctx.body() } returns """{"text": null}""" + + assertThrows(BadRequestResponse::class.java) { controller.create(ctx) } + } + + @Test + fun createNoJsonName() { + every { ctx.body() } returns "foo" assertThrows(BadRequestResponse::class.java) { controller.create(ctx) } } @Test fun createTooLongName() { - every { ctx.formParam("text") } returns getRandomString(MAX_FORUM_NAME_LENGTH + 1) + every { ctx.body() } returns """{"text": "${getRandomString(MAX_FORUM_NAME_LENGTH + 1)}"}""" assertThrows(BadRequestResponse::class.java) { controller.create(ctx) } } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt index c567e547c..70c865f69 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt @@ -95,7 +95,7 @@ internal class MessagingControllerImplTest : ControllerTest() { val slot = CapturingSlot() expectGetContact() - every { ctx.formParam("text") } returns body + every { ctx.body() } returns """{"text": "$body"}""" every { messagingManager.getContactGroup(contact) } returns group every { clock.currentTimeMillis() } returns timestamp every { @@ -126,7 +126,7 @@ internal class MessagingControllerImplTest : ControllerTest() { @Test fun writeNonexistentBody() { expectGetContact() - every { ctx.formParam("text") } returns null + every { ctx.body() } returns """{"foo": "bar"}""" assertThrows(BadRequestResponse::class.java) { controller.write(ctx) } } @@ -134,7 +134,7 @@ internal class MessagingControllerImplTest : ControllerTest() { @Test fun writeEmptyBody() { expectGetContact() - every { ctx.formParam("text") } returns "" + every { ctx.body() } returns """{"text": ""}""" assertThrows(BadRequestResponse::class.java) { controller.write(ctx) } } @@ -142,7 +142,7 @@ internal class MessagingControllerImplTest : ControllerTest() { @Test fun writeTooLongBody() { expectGetContact() - every { ctx.formParam("text") } returns getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1) + every { ctx.body() } returns """{"text": "${getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1)}"}""" assertThrows(BadRequestResponse::class.java) { controller.write(ctx) } }