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

[headless] address review comments for remote contact adding

parent faa6a851
No related branches found
No related tags found
1 merge request!1094Add a REST endpoint for adding contacts
Pipeline #3315 passed
...@@ -49,9 +49,8 @@ public class BriarTestUtils { ...@@ -49,9 +49,8 @@ public class BriarTestUtils {
public static String getRealHandshakeLink(CryptoComponent cryptoComponent) { public static String getRealHandshakeLink(CryptoComponent cryptoComponent) {
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair(); KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
byte[] linkBytes = new byte[RAW_LINK_BYTES]; byte[] linkBytes = new byte[RAW_LINK_BYTES];
byte[] version = new byte[] {FORMAT_VERSION};
byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] publicKey = keyPair.getPublic().getEncoded();
arraycopy(version,0, linkBytes, 0, 1); linkBytes[0] = FORMAT_VERSION;
arraycopy(publicKey,0, linkBytes, 1, RAW_LINK_BYTES - 1); arraycopy(publicKey,0, linkBytes, 1, RAW_LINK_BYTES - 1);
return ("briar://" + Base32.encode(linkBytes)).toLowerCase(); return ("briar://" + Base32.encode(linkBytes)).toLowerCase();
} }
......
...@@ -65,6 +65,8 @@ Returns a JSON array of contacts: ...@@ -65,6 +65,8 @@ Returns a JSON array of contacts:
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo=" "publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
}, },
"contactId": 1, "contactId": 1,
"alias" : "A local nickname",
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
"verified": true "verified": true
} }
``` ```
...@@ -282,14 +284,18 @@ it will send a JSON object representing the new contact to connected websocket c ...@@ -282,14 +284,18 @@ it will send a JSON object representing the new contact to connected websocket c
```json ```json
{ {
"data": { "data": {
"author": { "contact": {
"formatVersion": 1, "author": {
"id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=", "formatVersion": 1,
"name": "Test", "id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=",
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo=" "name": "Test",
}, "publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
"contactId": 1, },
"verified": true "contactId": 1,
"alias" : "A local nickname",
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
"verified": true
}
}, },
"name": "ContactAddedRemotelyEvent", "name": "ContactAddedRemotelyEvent",
"type": "event" "type": "event"
......
...@@ -8,7 +8,7 @@ fun Author.output() = JsonDict( ...@@ -8,7 +8,7 @@ fun Author.output() = JsonDict(
"formatVersion" to formatVersion, "formatVersion" to formatVersion,
"id" to id.bytes, "id" to id.bytes,
"name" to name, "name" to name,
"publicKey" to publicKey "publicKey" to publicKey.encoded
) )
fun AuthorInfo.Status.output() = name.toLowerCase() fun AuthorInfo.Status.output() = name.toLowerCase()
...@@ -66,7 +66,7 @@ constructor( ...@@ -66,7 +66,7 @@ constructor(
get { ctx -> contactController.list(ctx) } get { ctx -> contactController.list(ctx) }
path("add") { path("add") {
path("link") { path("link") {
get { ctx -> contactController.link(ctx) } get { ctx -> contactController.getLink(ctx) }
} }
path("pending") { path("pending") {
get { ctx -> contactController.listPendingContacts(ctx) } get { ctx -> contactController.listPendingContacts(ctx) }
......
...@@ -5,7 +5,7 @@ import io.javalin.Context ...@@ -5,7 +5,7 @@ import io.javalin.Context
interface ContactController { interface ContactController {
fun list(ctx: Context): Context fun list(ctx: Context): Context
fun link(ctx: Context): Context fun getLink(ctx: Context): Context
fun addPendingContact(ctx: Context): Context fun addPendingContact(ctx: Context): Context
fun listPendingContacts(ctx: Context): Context fun listPendingContacts(ctx: Context): Context
fun removePendingContact(ctx: Context): Context fun removePendingContact(ctx: Context): Context
......
package org.briarproject.briar.headless.contact package org.briarproject.briar.headless.contact
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import io.javalin.BadRequestResponse
import io.javalin.Context import io.javalin.Context
import io.javalin.NotFoundResponse import io.javalin.NotFoundResponse
import org.briarproject.bramble.api.contact.ContactManager import org.briarproject.bramble.api.contact.ContactManager
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX
import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.PendingContactId
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
...@@ -12,6 +14,8 @@ import org.briarproject.bramble.api.db.NoSuchContactException ...@@ -12,6 +14,8 @@ import org.briarproject.bramble.api.db.NoSuchContactException
import org.briarproject.bramble.api.db.NoSuchPendingContactException import org.briarproject.bramble.api.db.NoSuchPendingContactException
import org.briarproject.bramble.api.event.Event import org.briarproject.bramble.api.event.Event
import org.briarproject.bramble.api.event.EventListener import org.briarproject.bramble.api.event.EventListener
import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
import org.briarproject.bramble.util.StringUtils.toUtf8
import org.briarproject.briar.headless.event.WebSocketController import org.briarproject.briar.headless.event.WebSocketController
import org.briarproject.briar.headless.getContactIdFromPathParam import org.briarproject.briar.headless.getContactIdFromPathParam
import org.briarproject.briar.headless.getFromJson import org.briarproject.briar.headless.getFromJson
...@@ -57,7 +61,7 @@ constructor( ...@@ -57,7 +61,7 @@ constructor(
return ctx.json(contacts) return ctx.json(contacts)
} }
override fun link(ctx: Context): Context { override fun getLink(ctx: Context): Context {
val linkDict = JsonDict("link" to contactManager.handshakeLink) val linkDict = JsonDict("link" to contactManager.handshakeLink)
return ctx.json(linkDict) return ctx.json(linkDict)
} }
...@@ -65,6 +69,10 @@ constructor( ...@@ -65,6 +69,10 @@ constructor(
override fun addPendingContact(ctx: Context): Context { override fun addPendingContact(ctx: Context): Context {
val link = ctx.getFromJson(objectMapper, "link") val link = ctx.getFromJson(objectMapper, "link")
val alias = ctx.getFromJson(objectMapper, "alias") val alias = ctx.getFromJson(objectMapper, "alias")
if (!LINK_REGEX.matcher(link).find()) throw BadRequestResponse("Invalid Link")
val aliasUtf8 = toUtf8(alias)
if (aliasUtf8.isEmpty() || aliasUtf8.size > MAX_AUTHOR_NAME_LENGTH)
throw BadRequestResponse("Invalid Alias")
val pendingContact = contactManager.addPendingContact(link, alias) val pendingContact = contactManager.addPendingContact(link, alias)
return ctx.json(pendingContact.output()) return ctx.json(pendingContact.output())
} }
......
...@@ -8,7 +8,12 @@ import org.briarproject.briar.headless.json.JsonDict ...@@ -8,7 +8,12 @@ import org.briarproject.briar.headless.json.JsonDict
internal fun Contact.output() = JsonDict( internal fun Contact.output() = JsonDict(
"contactId" to id.int, "contactId" to id.int,
"author" to author.output(), "author" to author.output(),
"alias" to alias,
"verified" to isVerified "verified" to isVerified
) ).apply {
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
}
internal fun ContactAddedRemotelyEvent.output() = contact.output() internal fun ContactAddedRemotelyEvent.output() = JsonDict(
"contact" to contact.output()
)
package org.briarproject.briar.headless.contact package org.briarproject.briar.headless.contact
import io.javalin.BadRequestResponse
import io.javalin.NotFoundResponse import io.javalin.NotFoundResponse
import io.javalin.json.JavalinJson.toJson import io.javalin.json.JavalinJson.toJson
import io.mockk.Runs import io.mockk.Runs
...@@ -15,11 +16,14 @@ import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent ...@@ -15,11 +16,14 @@ import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
import org.briarproject.bramble.api.db.NoSuchContactException import org.briarproject.bramble.api.db.NoSuchContactException
import org.briarproject.bramble.api.db.NoSuchPendingContactException import org.briarproject.bramble.api.db.NoSuchPendingContactException
import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
import org.briarproject.bramble.identity.output import org.briarproject.bramble.identity.output
import org.briarproject.bramble.test.TestUtils.getPendingContact import org.briarproject.bramble.test.TestUtils.getPendingContact
import org.briarproject.bramble.test.TestUtils.getRandomBytes import org.briarproject.bramble.test.TestUtils.getRandomBytes
import org.briarproject.bramble.util.StringUtils.getRandomString
import org.briarproject.briar.headless.ControllerTest import org.briarproject.briar.headless.ControllerTest
import org.briarproject.briar.headless.json.JsonDict import org.briarproject.briar.headless.json.JsonDict
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
...@@ -49,12 +53,12 @@ internal class ContactControllerTest : ControllerTest() { ...@@ -49,12 +53,12 @@ internal class ContactControllerTest : ControllerTest() {
val link = "briar://link" val link = "briar://link"
every { contactManager.handshakeLink } returns link every { contactManager.handshakeLink } returns link
every { ctx.json(JsonDict("link" to link)) } returns ctx every { ctx.json(JsonDict("link" to link)) } returns ctx
controller.link(ctx) controller.getLink(ctx)
} }
@Test @Test
fun testAddPendingContact() { fun testAddPendingContact() {
val link = "briar://link123" val link = "briar://briar://adnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs"
val alias = "Alias123" val alias = "Alias123"
val body = """{ val body = """{
"link": "$link", "link": "$link",
...@@ -66,6 +70,58 @@ internal class ContactControllerTest : ControllerTest() { ...@@ -66,6 +70,58 @@ internal class ContactControllerTest : ControllerTest() {
controller.addPendingContact(ctx) controller.addPendingContact(ctx)
} }
@Test
fun testAddPendingContactInvalidLink() {
val link = "briar://link123"
val alias = "Alias123"
val body = """{
"link": "$link",
"alias": "$alias"
}"""
every { ctx.body() } returns body
assertThrows(BadRequestResponse::class.java) {
controller.addPendingContact(ctx)
}
}
@Test
fun testAddPendingContactMissingLink() {
val alias = "Alias123"
val body = """{
"alias": "$alias"
}"""
every { ctx.body() } returns body
assertThrows(BadRequestResponse::class.java) {
controller.addPendingContact(ctx)
}
}
@Test
fun testAddPendingContactInvalidAlias() {
val link = "briar://briar://adnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs"
val alias = getRandomString(MAX_AUTHOR_NAME_LENGTH + 1)
val body = """{
"link": "$link",
"alias": "$alias"
}"""
every { ctx.body() } returns body
assertThrows(BadRequestResponse::class.java) {
controller.addPendingContact(ctx)
}
}
@Test
fun testAddPendingContactMissingAlias() {
val link = "briar://adnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs"
val body = """{
"link": "$link"
}"""
every { ctx.body() } returns body
assertThrows(BadRequestResponse::class.java) {
controller.addPendingContact(ctx)
}
}
@Test @Test
fun testListPendingContacts() { fun testListPendingContacts() {
every { contactManager.pendingContacts } returns listOf(pendingContact) every { contactManager.pendingContacts } returns listOf(pendingContact)
...@@ -185,10 +241,13 @@ internal class ContactControllerTest : ControllerTest() { ...@@ -185,10 +241,13 @@ internal class ContactControllerTest : ControllerTest() {
@Test @Test
fun testOutputContact() { fun testOutputContact() {
assertNotNull(contact.handshakePublicKey)
val json = """ val json = """
{ {
"contactId": ${contact.id.int}, "contactId": ${contact.id.int},
"author": ${toJson(author.output())}, "author": ${toJson(author.output())},
"alias" : "${contact.alias}",
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
"verified": ${contact.isVerified} "verified": ${contact.isVerified}
} }
""" """
...@@ -202,7 +261,7 @@ internal class ContactControllerTest : ControllerTest() { ...@@ -202,7 +261,7 @@ internal class ContactControllerTest : ControllerTest() {
"formatVersion": 1, "formatVersion": 1,
"id": ${toJson(author.id.bytes)}, "id": ${toJson(author.id.bytes)},
"name": "${author.name}", "name": "${author.name}",
"publicKey": ${toJson(author.publicKey)} "publicKey": ${toJson(author.publicKey.encoded)}
} }
""" """
assertJsonEquals(json, author.output()) assertJsonEquals(json, author.output())
...@@ -211,7 +270,12 @@ internal class ContactControllerTest : ControllerTest() { ...@@ -211,7 +270,12 @@ internal class ContactControllerTest : ControllerTest() {
@Test @Test
fun testOutputContactAddedRemotelyEvent() { fun testOutputContactAddedRemotelyEvent() {
val event = ContactAddedRemotelyEvent(contact) val event = ContactAddedRemotelyEvent(contact)
assertJsonEquals(toJson(contact.output()), event.output()) val json = """
{
"contact": ${toJson(contact.output())}
}
"""
assertJsonEquals(json, event.output())
} }
@Test @Test
......
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