mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Merge branch '1256-adding-contacts-headless' into 'master'
Add a REST endpoint for adding contacts Closes #1256 See merge request briar/briar!1094
This commit is contained in:
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
@@ -36,7 +37,6 @@ import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||
@@ -181,8 +181,10 @@ public class TestUtils {
|
||||
PendingContactId id = new PendingContactId(getRandomId());
|
||||
PublicKey publicKey = getAgreementPublicKey();
|
||||
String alias = getRandomString(nameLength);
|
||||
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
|
||||
timestamp);
|
||||
int stateIndex =
|
||||
random.nextInt(PendingContactState.values().length - 1);
|
||||
PendingContactState state = PendingContactState.values()[stateIndex];
|
||||
return new PendingContact(id, publicKey, alias, state, timestamp);
|
||||
}
|
||||
|
||||
public static ContactId getContactId() {
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
@@ -293,6 +295,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
if (db.containsPendingContact(txn, p.getId()))
|
||||
throw new PendingContactExistsException();
|
||||
db.addPendingContact(txn, p);
|
||||
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
|
||||
p.getState()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -892,6 +896,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
if (!db.containsPendingContact(txn, p))
|
||||
throw new NoSuchPendingContactException();
|
||||
db.removePendingContact(txn, p);
|
||||
transaction.attach(new PendingContactRemovedEvent(p));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,3 +30,15 @@ dependencies {
|
||||
|
||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||
}
|
||||
|
||||
// needed to make test output available to briar-headless
|
||||
configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
}
|
||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
from sourceSets.test.output
|
||||
classifier = 'test'
|
||||
}
|
||||
artifacts {
|
||||
testOutput jarTest
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package org.briarproject.briar.test;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
|
||||
import static java.lang.System.arraycopy;
|
||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -40,4 +46,13 @@ public class BriarTestUtils {
|
||||
return authorFactory.createLocalAuthor(name);
|
||||
}
|
||||
|
||||
public static String getRealHandshakeLink(CryptoComponent cryptoComponent) {
|
||||
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
|
||||
byte[] linkBytes = new byte[RAW_LINK_BYTES];
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
linkBytes[0] = FORMAT_VERSION;
|
||||
arraycopy(publicKey,0, linkBytes, 1, RAW_LINK_BYTES - 1);
|
||||
return ("briar://" + Base32.encode(linkBytes)).toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -65,16 +65,87 @@ Returns a JSON array of contacts:
|
||||
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
|
||||
},
|
||||
"contactId": 1,
|
||||
"alias" : "A local nickname",
|
||||
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
||||
"verified": true
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a contact
|
||||
|
||||
*Not yet implemented*
|
||||
The first step is to get your own link:
|
||||
|
||||
The only workaround is to add a contact to the Briar app running on a rooted Android phone
|
||||
and then move its database (and key files) to the headless peer.
|
||||
`GET /v1/contacts/add/link`
|
||||
|
||||
Returns a JSON object with a `briar://` link that needs to be sent to the contact you want to add
|
||||
outside of Briar via an external channel.
|
||||
|
||||
```json
|
||||
{
|
||||
"link": "briar://wvui4uvhbfv4tzo6xwngknebsxrafainnhldyfj63x6ipp4q2vigy"
|
||||
}
|
||||
```
|
||||
|
||||
Once you have received the link of your future contact, you can add them
|
||||
by posting the link together with an arbitrary nickname (or alias):
|
||||
|
||||
`POST /v1/contacts/add/pending`
|
||||
|
||||
The link and the alias should be posted as a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"link": "briar://ddnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs",
|
||||
"alias": "A nickname for the new contact"
|
||||
}
|
||||
```
|
||||
|
||||
This starts the process of adding the contact.
|
||||
Until it is completed, a pending contact is returned as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
|
||||
"alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
|
||||
"state": "adding_contact",
|
||||
"timestamp": 1557838312175
|
||||
}
|
||||
```
|
||||
|
||||
The state can be one of these values:
|
||||
|
||||
* `waiting_for_connection`
|
||||
* `connected`
|
||||
* `adding_contact`
|
||||
* `failed`
|
||||
|
||||
If you want to get informed about state changes,
|
||||
you can use the Websocket API (below) to listen for events.
|
||||
|
||||
The following events are relevant here:
|
||||
|
||||
* `PendingContactStateChangedEvent`
|
||||
* `PendingContactRemovedEvent`
|
||||
* `ContactAddedRemotelyEvent` (when the pending contact becomes an actual contact)
|
||||
|
||||
It is possible to get a list of all pending contacts:
|
||||
|
||||
`GET /v1/contacts/add/pending`
|
||||
|
||||
This will return a JSON array of pending contacts formatted as shown above.
|
||||
|
||||
To remove a pending contact and abort the process of adding it:
|
||||
|
||||
`DELETE /v1/contacts/add/pending`
|
||||
|
||||
The `pendingContactId` of the pending contact to delete
|
||||
needs to be provided in the request body as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U="
|
||||
}
|
||||
```
|
||||
|
||||
### Removing a contact
|
||||
|
||||
@@ -204,3 +275,56 @@ it will send a JSON object to connected websocket clients:
|
||||
|
||||
Note that the JSON object in `data` is exactly what the REST API returns
|
||||
when listing private messages.
|
||||
|
||||
### A new contact was added remotely
|
||||
|
||||
When the Briar peer adds a new contact remotely,
|
||||
it will send a JSON object representing the new contact to connected websocket clients:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"contact": {
|
||||
"author": {
|
||||
"formatVersion": 1,
|
||||
"id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=",
|
||||
"name": "Test",
|
||||
"publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
|
||||
},
|
||||
"contactId": 1,
|
||||
"alias" : "A local nickname",
|
||||
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
||||
"verified": true
|
||||
}
|
||||
},
|
||||
"name": "ContactAddedRemotelyEvent",
|
||||
"type": "event"
|
||||
}
|
||||
```
|
||||
|
||||
### A pending contact changed its state
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"pendingContactId":"YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M=",
|
||||
"state":"waiting_for_connection"
|
||||
},
|
||||
"name": "PendingContactStateChangedEvent",
|
||||
"type": "event"
|
||||
}
|
||||
```
|
||||
|
||||
For a list of valid states, please see the section on adding contacts above.
|
||||
|
||||
### A pending contact was removed
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"pendingContactId": "YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M="
|
||||
},
|
||||
"name": "PendingContactRemovedEvent",
|
||||
"type": "event"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.21'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.3.21'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.31'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.3.31'
|
||||
id 'witness'
|
||||
}
|
||||
apply from: 'witness.gradle'
|
||||
@@ -14,25 +14,28 @@ dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-java', configuration: 'default')
|
||||
|
||||
implementation 'io.javalin:javalin:2.7.0'
|
||||
implementation 'org.slf4j:slf4j-simple:1.7.25'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.31'
|
||||
implementation 'io.javalin:javalin:2.8.0'
|
||||
implementation 'org.slf4j:slf4j-simple:1.7.26'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
|
||||
implementation 'com.github.ajalt:clikt:1.6.0'
|
||||
implementation 'com.github.ajalt:clikt:2.0.0'
|
||||
|
||||
kapt 'com.google.dagger:dagger-compiler:2.22.1'
|
||||
def daggerVersion = '2.22.1'
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
testImplementation project(path: ':briar-core', configuration: 'testOutput')
|
||||
|
||||
def junitVersion = '5.3.1'
|
||||
def junitVersion = '5.4.2'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
testImplementation "io.mockk:mockk:1.9.2"
|
||||
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
||||
testImplementation 'io.mockk:mockk:1.9.3'
|
||||
testImplementation 'org.skyscreamer:jsonassert:1.5.0'
|
||||
testImplementation 'khttp:khttp:0.1.0'
|
||||
|
||||
kaptTest 'com.google.dagger:dagger-compiler:2.22.1'
|
||||
kaptTest "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
}
|
||||
|
||||
jar {
|
||||
|
||||
@@ -8,7 +8,7 @@ fun Author.output() = JsonDict(
|
||||
"formatVersion" to formatVersion,
|
||||
"id" to id.bytes,
|
||||
"name" to name,
|
||||
"publicKey" to publicKey
|
||||
"publicKey" to publicKey.encoded
|
||||
)
|
||||
|
||||
fun AuthorInfo.Status.output() = name.toLowerCase()
|
||||
|
||||
@@ -64,6 +64,16 @@ constructor(
|
||||
path("/v1") {
|
||||
path("/contacts") {
|
||||
get { ctx -> contactController.list(ctx) }
|
||||
path("add") {
|
||||
path("link") {
|
||||
get { ctx -> contactController.getLink(ctx) }
|
||||
}
|
||||
path("pending") {
|
||||
get { ctx -> contactController.listPendingContacts(ctx) }
|
||||
post { ctx -> contactController.addPendingContact(ctx) }
|
||||
delete { ctx -> contactController.removePendingContact(ctx) }
|
||||
}
|
||||
}
|
||||
path("/:contactId") {
|
||||
delete { ctx -> contactController.delete(ctx) }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import io.javalin.Context
|
||||
interface ContactController {
|
||||
|
||||
fun list(ctx: Context): Context
|
||||
fun getLink(ctx: Context): Context
|
||||
fun addPendingContact(ctx: Context): Context
|
||||
fun listPendingContacts(ctx: Context): Context
|
||||
fun removePendingContact(ctx: Context): Context
|
||||
fun delete(ctx: Context): Context
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,58 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.javalin.BadRequestResponse
|
||||
import io.javalin.Context
|
||||
import io.javalin.NotFoundResponse
|
||||
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.event.ContactAddedRemotelyEvent
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException
|
||||
import org.briarproject.bramble.api.event.Event
|
||||
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.getContactIdFromPathParam
|
||||
import org.briarproject.briar.headless.getFromJson
|
||||
import org.briarproject.briar.headless.json.JsonDict
|
||||
import org.spongycastle.util.encoders.Base64
|
||||
import org.spongycastle.util.encoders.DecoderException
|
||||
import javax.annotation.concurrent.Immutable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
internal const val EVENT_CONTACT_ADDED_REMOTELY = "ContactAddedRemotelyEvent"
|
||||
internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent"
|
||||
internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent"
|
||||
|
||||
@Immutable
|
||||
@Singleton
|
||||
internal class ContactControllerImpl
|
||||
@Inject
|
||||
constructor(private val contactManager: ContactManager) : ContactController {
|
||||
constructor(
|
||||
private val contactManager: ContactManager,
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val webSocket: WebSocketController
|
||||
) : ContactController, EventListener {
|
||||
|
||||
override fun eventOccurred(e: Event) = when (e) {
|
||||
is ContactAddedRemotelyEvent -> {
|
||||
webSocket.sendEvent(EVENT_CONTACT_ADDED_REMOTELY, e.output())
|
||||
}
|
||||
is PendingContactStateChangedEvent -> {
|
||||
webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output())
|
||||
}
|
||||
is PendingContactRemovedEvent -> {
|
||||
webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output())
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun list(ctx: Context): Context {
|
||||
val contacts = contactManager.contacts.map { contact ->
|
||||
@@ -22,6 +61,48 @@ constructor(private val contactManager: ContactManager) : ContactController {
|
||||
return ctx.json(contacts)
|
||||
}
|
||||
|
||||
override fun getLink(ctx: Context): Context {
|
||||
val linkDict = JsonDict("link" to contactManager.handshakeLink)
|
||||
return ctx.json(linkDict)
|
||||
}
|
||||
|
||||
override fun addPendingContact(ctx: Context): Context {
|
||||
val link = ctx.getFromJson(objectMapper, "link")
|
||||
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)
|
||||
return ctx.json(pendingContact.output())
|
||||
}
|
||||
|
||||
override fun listPendingContacts(ctx: Context): Context {
|
||||
val pendingContacts = contactManager.pendingContacts.map { pendingContact ->
|
||||
pendingContact.output()
|
||||
}
|
||||
return ctx.json(pendingContacts)
|
||||
}
|
||||
|
||||
override fun removePendingContact(ctx: Context): Context {
|
||||
// construct and check PendingContactId
|
||||
val pendingContactString = ctx.getFromJson(objectMapper, "pendingContactId")
|
||||
val pendingContactBytes = try {
|
||||
Base64.decode(pendingContactString)
|
||||
} catch (e: DecoderException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
if (pendingContactBytes.size != PendingContactId.LENGTH) throw NotFoundResponse()
|
||||
val id = PendingContactId(pendingContactBytes)
|
||||
// remove
|
||||
try {
|
||||
contactManager.removePendingContact(id)
|
||||
} catch (e: NoSuchPendingContactException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
override fun delete(ctx: Context): Context {
|
||||
val contactId = ctx.getContactIdFromPathParam()
|
||||
try {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.headless.contact
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.briarproject.bramble.api.event.EventBus
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@@ -9,7 +10,11 @@ class HeadlessContactModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideContactController(contactController: ContactControllerImpl): ContactController {
|
||||
internal fun provideContactController(
|
||||
eventBus: EventBus,
|
||||
contactController: ContactControllerImpl
|
||||
): ContactController {
|
||||
eventBus.addListener(contactController)
|
||||
return contactController
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
|
||||
import org.briarproject.bramble.identity.output
|
||||
import org.briarproject.briar.headless.json.JsonDict
|
||||
|
||||
@@ -8,4 +9,11 @@ internal fun Contact.output() = JsonDict(
|
||||
"contactId" to id.int,
|
||||
"author" to author.output(),
|
||||
"verified" to isVerified
|
||||
)
|
||||
).apply {
|
||||
alias?.let { put("alias", it) }
|
||||
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
|
||||
}
|
||||
|
||||
internal fun ContactAddedRemotelyEvent.output() = JsonDict(
|
||||
"contact" to contact.output()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import org.briarproject.bramble.api.contact.PendingContact
|
||||
import org.briarproject.bramble.api.contact.PendingContactState
|
||||
import org.briarproject.bramble.api.contact.PendingContactState.*
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||
import org.briarproject.briar.headless.json.JsonDict
|
||||
|
||||
internal fun PendingContact.output() = JsonDict(
|
||||
"pendingContactId" to id.bytes,
|
||||
"alias" to alias,
|
||||
"state" to state.output(),
|
||||
"timestamp" to timestamp
|
||||
)
|
||||
|
||||
internal fun PendingContactState.output() = when(this) {
|
||||
WAITING_FOR_CONNECTION -> "waiting_for_connection"
|
||||
CONNECTED -> "connected"
|
||||
ADDING_CONTACT -> "adding_contact"
|
||||
FAILED -> "failed"
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
|
||||
internal fun PendingContactStateChangedEvent.output() = JsonDict(
|
||||
"pendingContactId" to id.bytes,
|
||||
"state" to pendingContactState.output()
|
||||
)
|
||||
|
||||
internal fun PendingContactRemovedEvent.output() = JsonDict(
|
||||
"pendingContactId" to id.bytes
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import dagger.Component
|
||||
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
||||
import org.briarproject.bramble.BrambleCoreModule
|
||||
import org.briarproject.bramble.account.AccountModule
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent
|
||||
import org.briarproject.bramble.event.DefaultEventExecutorModule
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule
|
||||
import org.briarproject.briar.BriarCoreEagerSingletons
|
||||
@@ -25,5 +26,7 @@ import javax.inject.Singleton
|
||||
internal interface BriarHeadlessTestApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons {
|
||||
fun getRouter(): Router
|
||||
|
||||
fun getCryptoComponent(): CryptoComponent
|
||||
|
||||
fun getTestDataCreator(): TestDataCreator
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.sync.Message
|
||||
import org.briarproject.bramble.api.system.Clock
|
||||
import org.briarproject.bramble.test.TestUtils.*
|
||||
import org.briarproject.bramble.util.StringUtils.getRandomString
|
||||
import org.briarproject.briar.headless.event.WebSocketController
|
||||
import org.skyscreamer.jsonassert.JSONAssert.assertEquals
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode.STRICT
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
@@ -26,6 +27,8 @@ abstract class ControllerTest {
|
||||
protected val clock = mockk<Clock>()
|
||||
protected val ctx = mockk<Context>()
|
||||
|
||||
protected val webSocketController = mockk<WebSocketController>()
|
||||
|
||||
private val request = mockk<HttpServletRequest>(relaxed = true)
|
||||
private val response = mockk<HttpServletResponse>(relaxed = true)
|
||||
private val outputCtx = ContextUtil.init(request, response)
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.javalin.Javalin
|
||||
import io.javalin.core.util.Header.AUTHORIZATION
|
||||
import khttp.responses.Response
|
||||
import org.briarproject.bramble.BrambleCoreModule
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent
|
||||
import org.briarproject.briar.BriarCoreModule
|
||||
import org.briarproject.briar.api.test.TestDataCreator
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
@@ -22,6 +23,7 @@ abstract class IntegrationTest {
|
||||
private val dataDir = File("tmp")
|
||||
|
||||
protected lateinit var api: Javalin
|
||||
protected lateinit var crypto: CryptoComponent
|
||||
protected lateinit var testDataCreator: TestDataCreator
|
||||
private lateinit var router: Router
|
||||
|
||||
@@ -33,6 +35,7 @@ abstract class IntegrationTest {
|
||||
BrambleCoreModule.initEagerSingletons(app)
|
||||
BriarCoreModule.initEagerSingletons(app)
|
||||
router = app.getRouter()
|
||||
crypto = app.getCryptoComponent()
|
||||
testDataCreator = app.getTestDataCreator()
|
||||
|
||||
api = router.start(token, port, false)
|
||||
@@ -52,10 +55,22 @@ abstract class IntegrationTest {
|
||||
return khttp.get(url, getAuthTokenHeader("wrongToken"))
|
||||
}
|
||||
|
||||
protected fun post(url: String, data: String) : Response {
|
||||
return khttp.post(url, getAuthTokenHeader(token), data = data)
|
||||
}
|
||||
|
||||
protected fun postWithWrongToken(url: String) : Response {
|
||||
return khttp.post(url, getAuthTokenHeader("wrongToken"), data = "")
|
||||
}
|
||||
|
||||
protected fun delete(url: String) : Response {
|
||||
return khttp.delete(url, getAuthTokenHeader(token))
|
||||
}
|
||||
|
||||
protected fun delete(url: String, data: String) : Response {
|
||||
return khttp.delete(url, getAuthTokenHeader(token), data = data)
|
||||
}
|
||||
|
||||
protected fun deleteWithWrongToken(url: String) : Response {
|
||||
return khttp.delete(url, getAuthTokenHeader("wrongToken"))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES
|
||||
import org.briarproject.briar.headless.IntegrationTest
|
||||
import org.briarproject.briar.headless.url
|
||||
import org.briarproject.briar.test.BriarTestUtils.getRealHandshakeLink
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ContactControllerIntegrationTest: IntegrationTest() {
|
||||
@@ -33,6 +36,75 @@ class ContactControllerIntegrationTest: IntegrationTest() {
|
||||
assertEquals(testContactName, author.getString("name"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns own handshake link`() {
|
||||
val response = get("$url/contacts/add/link")
|
||||
assertEquals(200, response.statusCode)
|
||||
val link = response.jsonObject.getString("link")
|
||||
assertTrue(link.startsWith("briar://"))
|
||||
assertEquals(BASE32_LINK_BYTES + 8, link.length)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returning own handshake link needs authentication token`() {
|
||||
val response = getWithWrongToken("$url/contacts/add/link")
|
||||
assertEquals(401, response.statusCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns list of pending contacts`() {
|
||||
// retrieve empty list of pending contacts
|
||||
var response = get("$url/contacts/add/pending")
|
||||
assertEquals(200, response.statusCode)
|
||||
assertEquals(0, response.jsonArray.length())
|
||||
|
||||
// add one pending contact
|
||||
val alias = "AliasFoo"
|
||||
val json = """{
|
||||
"link": "${getRealHandshakeLink(crypto)}",
|
||||
"alias": "$alias"
|
||||
}"""
|
||||
response = post("$url/contacts/add/pending", json)
|
||||
assertEquals(200, response.statusCode)
|
||||
|
||||
// get added contact as only list item
|
||||
response = get("$url/contacts/add/pending")
|
||||
assertEquals(200, response.statusCode)
|
||||
assertEquals(1, response.jsonArray.length())
|
||||
val jsonObject = response.jsonArray.getJSONObject(0)
|
||||
assertEquals(alias, jsonObject.getString("alias"))
|
||||
assertEquals("waiting_for_connection", jsonObject.getString("state"))
|
||||
|
||||
// remove pending contact again
|
||||
val idString = jsonObject.getString("pendingContactId")
|
||||
val deleteJson = """{"pendingContactId": "$idString"}"""
|
||||
response = delete("$url/contacts/add/pending", deleteJson)
|
||||
assertEquals(200, response.statusCode)
|
||||
|
||||
// list of pending contacts should be empty now
|
||||
response = get("$url/contacts/add/pending")
|
||||
assertEquals(200, response.statusCode)
|
||||
assertEquals(0, response.jsonArray.length())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returning list of pending contacts needs authentication token`() {
|
||||
val response = getWithWrongToken("$url/contacts/add/pending")
|
||||
assertEquals(401, response.statusCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adding pending contacts needs authentication token`() {
|
||||
val response = postWithWrongToken("$url/contacts/add/pending")
|
||||
assertEquals(401, response.statusCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `removing a pending contact needs authentication token`() {
|
||||
val response = deleteWithWrongToken("$url/contacts/add/pending")
|
||||
assertEquals(401, response.statusCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleting contact need authentication token`() {
|
||||
val response = deleteWithWrongToken("$url/contacts/1")
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import io.javalin.BadRequestResponse
|
||||
import io.javalin.NotFoundResponse
|
||||
import io.javalin.json.JavalinJson.toJson
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.runs
|
||||
import org.briarproject.bramble.api.contact.Contact
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.bramble.api.contact.PendingContactId
|
||||
import org.briarproject.bramble.api.contact.PendingContactState.FAILED
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
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.test.TestUtils.getPendingContact
|
||||
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.json.JsonDict
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class ContactControllerTest : ControllerTest() {
|
||||
|
||||
private val controller = ContactControllerImpl(contactManager)
|
||||
private val pendingContact = getPendingContact()
|
||||
|
||||
private val controller =
|
||||
ContactControllerImpl(contactManager, objectMapper, webSocketController)
|
||||
|
||||
@Test
|
||||
fun testEmptyContactList() {
|
||||
@@ -31,6 +48,131 @@ internal class ContactControllerTest : ControllerTest() {
|
||||
controller.list(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLink() {
|
||||
val link = "briar://link"
|
||||
every { contactManager.handshakeLink } returns link
|
||||
every { ctx.json(JsonDict("link" to link)) } returns ctx
|
||||
controller.getLink(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddPendingContact() {
|
||||
val link = "briar://briar://adnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs"
|
||||
val alias = "Alias123"
|
||||
val body = """{
|
||||
"link": "$link",
|
||||
"alias": "$alias"
|
||||
}"""
|
||||
every { ctx.body() } returns body
|
||||
every { contactManager.addPendingContact(link, alias) } returns pendingContact
|
||||
every { ctx.json(pendingContact.output()) } returns 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
|
||||
fun testListPendingContacts() {
|
||||
every { contactManager.pendingContacts } returns listOf(pendingContact)
|
||||
every { ctx.json(listOf(pendingContact.output())) } returns ctx
|
||||
controller.listPendingContacts(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePendingContact() {
|
||||
val id = pendingContact.id
|
||||
every { ctx.body() } returns """{"pendingContactId": ${toJson(id.bytes)}}"""
|
||||
every { contactManager.removePendingContact(id) } just Runs
|
||||
controller.removePendingContact(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePendingContactInvalidId() {
|
||||
every { ctx.body() } returns """{"pendingContactId": "foo"}"""
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.removePendingContact(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePendingContactTooShortId() {
|
||||
val bytes = getRandomBytes(PendingContactId.LENGTH - 1)
|
||||
every { ctx.body() } returns """{"pendingContactId": ${toJson(bytes)}}"""
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.removePendingContact(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePendingContactTooLongId() {
|
||||
val bytes = getRandomBytes(PendingContactId.LENGTH + 1)
|
||||
every { ctx.body() } returns """{"pendingContactId": ${toJson(bytes)}}"""
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.removePendingContact(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePendingContactNonexistentId() {
|
||||
val id = pendingContact.id
|
||||
every { ctx.body() } returns """{"pendingContactId": ${toJson(id.bytes)}}"""
|
||||
every { contactManager.removePendingContact(id) } throws NoSuchPendingContactException()
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.removePendingContact(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
every { ctx.pathParam("contactId") } returns "1"
|
||||
@@ -55,12 +197,57 @@ internal class ContactControllerTest : ControllerTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testContactAddedRemotelyEvent() {
|
||||
val event = ContactAddedRemotelyEvent(contact)
|
||||
|
||||
every {
|
||||
webSocketController.sendEvent(
|
||||
EVENT_CONTACT_ADDED_REMOTELY,
|
||||
event.output()
|
||||
)
|
||||
} just runs
|
||||
|
||||
controller.eventOccurred(event)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPendingContactStateChangedEvent() {
|
||||
val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)
|
||||
|
||||
every {
|
||||
webSocketController.sendEvent(
|
||||
EVENT_PENDING_CONTACT_STATE_CHANGED,
|
||||
event.output()
|
||||
)
|
||||
} just runs
|
||||
|
||||
controller.eventOccurred(event)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPendingContactRemovedEvent() {
|
||||
val event = PendingContactRemovedEvent(pendingContact.id)
|
||||
|
||||
every {
|
||||
webSocketController.sendEvent(
|
||||
EVENT_PENDING_CONTACT_REMOVED,
|
||||
event.output()
|
||||
)
|
||||
} just runs
|
||||
|
||||
controller.eventOccurred(event)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputContact() {
|
||||
assertNotNull(contact.handshakePublicKey)
|
||||
val json = """
|
||||
{
|
||||
"contactId": ${contact.id.int},
|
||||
"author": ${toJson(author.output())},
|
||||
"alias" : "${contact.alias}",
|
||||
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
|
||||
"verified": ${contact.isVerified}
|
||||
}
|
||||
"""
|
||||
@@ -74,10 +261,57 @@ internal class ContactControllerTest : ControllerTest() {
|
||||
"formatVersion": 1,
|
||||
"id": ${toJson(author.id.bytes)},
|
||||
"name": "${author.name}",
|
||||
"publicKey": ${toJson(author.publicKey)}
|
||||
"publicKey": ${toJson(author.publicKey.encoded)}
|
||||
}
|
||||
"""
|
||||
assertJsonEquals(json, author.output())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputContactAddedRemotelyEvent() {
|
||||
val event = ContactAddedRemotelyEvent(contact)
|
||||
val json = """
|
||||
{
|
||||
"contact": ${toJson(contact.output())}
|
||||
}
|
||||
"""
|
||||
assertJsonEquals(json, event.output())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputPendingContact() {
|
||||
val json = """
|
||||
{
|
||||
"pendingContactId": ${toJson(pendingContact.id.bytes)},
|
||||
"alias": "${pendingContact.alias}",
|
||||
"state": "${pendingContact.state.name.toLowerCase()}",
|
||||
"timestamp": ${pendingContact.timestamp}
|
||||
}
|
||||
"""
|
||||
assertJsonEquals(json, pendingContact.output())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputPendingContactStateChangedEvent() {
|
||||
val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)
|
||||
val json = """
|
||||
{
|
||||
"pendingContactId": ${toJson(pendingContact.id.bytes)},
|
||||
"state": "failed"
|
||||
}
|
||||
"""
|
||||
assertJsonEquals(json, event.output())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputPendingContactRemovedEvent() {
|
||||
val event = PendingContactRemovedEvent(pendingContact.id)
|
||||
val json = """
|
||||
{
|
||||
"pendingContactId": ${toJson(pendingContact.id.bytes)}
|
||||
}
|
||||
"""
|
||||
assertJsonEquals(json, event.output())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.briarproject.briar.api.messaging.PrivateMessageFactory
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
||||
import org.briarproject.briar.headless.ControllerTest
|
||||
import org.briarproject.briar.headless.event.WebSocketController
|
||||
import org.briarproject.briar.headless.event.output
|
||||
import org.briarproject.briar.headless.json.JsonDict
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
@@ -35,7 +34,6 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
||||
private val messagingManager = mockk<MessagingManager>()
|
||||
private val conversationManager = mockk<ConversationManager>()
|
||||
private val privateMessageFactory = mockk<PrivateMessageFactory>()
|
||||
private val webSocketController = mockk<WebSocketController>()
|
||||
private val dbExecutor = ImmediateExecutor()
|
||||
|
||||
private val controller = MessagingControllerImpl(
|
||||
|
||||
@@ -3,7 +3,7 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.9.0:jackson-annotations-2.9.0.jar:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a',
|
||||
'com.fasterxml.jackson.core:jackson-core:2.9.8:jackson-core-2.9.8.jar:d934dab0bd48994eeea2c1b493cb547158a338a80b58c4fbc8e85fb0905e105f',
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.9.8:jackson-databind-2.9.8.jar:2351c3eba73a545db9079f5d6d768347ad72666537362c8220fe3e950a55a864',
|
||||
'com.github.ajalt:clikt:1.6.0:clikt-1.6.0.jar:ebab34d5a60817bb7d471a67cd1740b91a5d99e224660bddbcf32bac1651a575',
|
||||
'com.github.ajalt:clikt:2.0.0:clikt-2.0.0.jar:c247adb96337e0799bf6d84f4c494df9d8f1e46e9157eacaf438d03323ee9475',
|
||||
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
||||
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
|
||||
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
|
||||
@@ -16,20 +16,20 @@ dependencyVerification {
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
|
||||
'com.vaadin.external.google:android-json:0.0.20131108.vaadin1:android-json-0.0.20131108.vaadin1.jar:dfb7bae2f404cfe0b72b4d23944698cb716b7665171812a0a4d0f5926c0fac79',
|
||||
'io.javalin:javalin:2.7.0:javalin-2.7.0.jar:0f345ea86419813b2ba45a6d64d284f15099ced9a6ce51cae815e4caec724429',
|
||||
'io.mockk:mockk-agent-api:1.9.2:mockk-agent-api-1.9.2.jar:396d56cdba086c1bf01d4402591bb7b8fe46de75110c627eb88d2cd3e58bf6f5',
|
||||
'io.mockk:mockk-agent-common:1.9.2:mockk-agent-common-1.9.2.jar:ac8ab7a568df79ec80449b05c633458397acf40e8ae5db58cd3966d463509ebc',
|
||||
'io.mockk:mockk-agent-jvm:1.9.2:mockk-agent-jvm-1.9.2.jar:acf0336dc1802cf70450b594adc8f61dce2a6942e8162789dab60f1b54256ae1',
|
||||
'io.mockk:mockk-common:1.9.2:mockk-common-1.9.2.jar:0f936f82427b0c7822cae8e303cdbd8ceee6204bed80eab3f18cf00f4b9b82a3',
|
||||
'io.mockk:mockk-dsl-jvm:1.9.2:mockk-dsl-jvm-1.9.2.jar:6f7a3093f05876b24b26db3b6d6a568e0c20253489a588dbc9c67b43eb838ad0',
|
||||
'io.mockk:mockk-dsl:1.9.2:mockk-dsl-1.9.2.jar:d681ad3a7063a2c7fb8f0164b8eb2fd0085fd9fb77af1a4f189039e73f2ec3c4',
|
||||
'io.mockk:mockk:1.9.2:mockk-1.9.2.jar:96ce20b64c6d05218fed0b99e93b9b49c2c3f40dd96273b53f2e448a598b1c57',
|
||||
'io.javalin:javalin:2.8.0:javalin-2.8.0.jar:1f2f8e60ba06b2d65058a4ca430fe74ba74c27c93b35c96a9c883bd960d6fb3f',
|
||||
'io.mockk:mockk-agent-api:1.9.3:mockk-agent-api-1.9.3.jar:90b9b54158ad31aafa414cb7889bd5a9b70b23e990c5a72eb0c17c3322e6d12d',
|
||||
'io.mockk:mockk-agent-common:1.9.3:mockk-agent-common-1.9.3.jar:a9ddd89f1e1393aa4b7e99d0032b961088bb8d51e48ff188ada3d1fa05696c88',
|
||||
'io.mockk:mockk-agent-jvm:1.9.3:mockk-agent-jvm-1.9.3.jar:4e0661778c531d2849d9636f7896bbba314307fb45b47a0107f6a7ad31d1d531',
|
||||
'io.mockk:mockk-common:1.9.3:mockk-common-1.9.3.jar:05b6d77650171b13194dd0edcc36656897d04267e85e9e89c4ec187bdaaa6a3d',
|
||||
'io.mockk:mockk-dsl-jvm:1.9.3:mockk-dsl-jvm-1.9.3.jar:86c5c158640d244d19b29e894827e9d8c27741b4e13ed2ed3bb54b7a4ee4220f',
|
||||
'io.mockk:mockk-dsl:1.9.3:mockk-dsl-1.9.3.jar:1ccb814a192a5e4d2c59369ddc2499e8417f49ec9834e4f3dc4619877fd6069a',
|
||||
'io.mockk:mockk:1.9.3:mockk-1.9.3.jar:875ec9f02fa42231510cade8c677b8598d9a0f5687b5cb25a1f188c1c41ef332',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'javax.servlet:javax.servlet-api:3.1.0:javax.servlet-api-3.1.0.jar:af456b2dd41c4e82cf54f3e743bc678973d9fe35bd4d3071fa05c7e5333b8482',
|
||||
'khttp:khttp:0.1.0:khttp-0.1.0.jar:48ab3bd22e461f2c2e74e3446d8f9568e24aab157f61fdc85ded6c0bfbe9a926',
|
||||
'net.bytebuddy:byte-buddy-agent:1.9.3:byte-buddy-agent-1.9.3.jar:547288e013a9d1f4a4ce2ab84c24e3edda6e433c7fa6b2c3c3613932671b05b1',
|
||||
'net.bytebuddy:byte-buddy:1.9.3:byte-buddy-1.9.3.jar:a27350be602caea67a33d31281496c84c69b5ab34ddc228e9ff2253fc8f9cd31',
|
||||
'net.bytebuddy:byte-buddy-agent:1.9.10:byte-buddy-agent-1.9.10.jar:8ed739d29132103250d307d2e8e3c95f07588ef0543ab11d2881d00768a5e182',
|
||||
'net.bytebuddy:byte-buddy:1.9.10:byte-buddy-1.9.10.jar:2936debc4d7b6c534848d361412e2d0f8bd06f7f27a6f4e728a20e97648d2bf3',
|
||||
'org.apiguardian:apiguardian-api:1.0.0:apiguardian-api-1.0.0.jar:1f58b77470d8d147a0538d515347dd322f49a83b9e884b8970051160464b65b3',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
||||
@@ -48,37 +48,35 @@ dependencyVerification {
|
||||
'org.eclipse.jetty:jetty-webapp:9.4.15.v20190215:jetty-webapp-9.4.15.v20190215.jar:81b56aa7c29513654827adc48e786f121b54183791c132255195b9a45d83a0f3',
|
||||
'org.eclipse.jetty:jetty-xml:9.4.15.v20190215:jetty-xml-9.4.15.v20190215.jar:c6d97a70572d5400e9ff3b7e32d4a4fd1c61319cbf997655a608064a75466082',
|
||||
'org.jetbrains.intellij.deps:trove4j:1.0.20181211:trove4j-1.0.20181211.jar:affb7c85a3c87bdcf69ff1dbb84de11f63dc931293934bc08cd7ab18de083601',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.3.21:kotlin-android-extensions-1.3.21.jar:2b0462ac3e4b36dffdb3bfa6173cb41b0e24e25a7d7eee1012471f1d27aea2dd',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.3.21:kotlin-annotation-processing-gradle-1.3.21.jar:faf880315d4fd6a666cc17aa5e9608c7468c70a279b49ccca67dba2a54adf692',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.3.21:kotlin-build-common-1.3.21.jar:f4d8d08c6f5966d9d517ced60c5224c7edca2d811ea0a702bd7199a00dd4fa25',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.21:kotlin-compiler-embeddable-1.3.21.jar:afaaedc324fbf6394d9f39544efcc93cfc59f8a5aa1a1a5c71d61e2483666c6a',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.3.21:kotlin-compiler-runner-1.3.21.jar:73e7088a074f9c517cd4bb2a8611834168459661c832136cf3628ccd5994cc3b',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.3.21:kotlin-daemon-client-1.3.21.jar:b3ecce11ec7b311ee0d1ccc65e811f3748f328010765e86cbdb29b2b70f73f1c',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.3.21:kotlin-gradle-plugin-api-1.3.21.jar:ed0ab11437310cd409657c5e5f8a6bf589af0a8348577cd600f54601fc97c369',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.3.21:kotlin-gradle-plugin-model-1.3.21.jar:fbade67a2a3fb234e2d4c1b8f07b2af6c096993f34ed732fe6fadaf696bc208a',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.21:kotlin-gradle-plugin-1.3.21.jar:7858c58f4c678a8416520f4c094282a481981cfe702d23121118c9c7e9ad2326',
|
||||
'org.jetbrains.kotlin:kotlin-native-utils:1.3.21:kotlin-native-utils-1.3.21.jar:406010a39f4c8cdd2351cc1110b98ed804c0aa810cb6106e7b9f4f2bcc21cd47',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.3.31:kotlin-android-extensions-1.3.31.jar:2f849616dcf5a5aa372e6c11ccd196607f0c3d42dd0a9be6d49ee3732ca050ba',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.3.31:kotlin-annotation-processing-gradle-1.3.31.jar:29a5fb59416226e2326f9fcb3ad0974915a424eec9125449981e1b9bbd9b79d6',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.3.31:kotlin-build-common-1.3.31.jar:a37bace5fce25dade884ea75972fcf2a67d6f1326bf300eca27d052423773267',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.31:kotlin-compiler-embeddable-1.3.31.jar:b7918cbce747683905486ae54e664fe5d5db60e8ed1cbfebc00c79912b9aaffd',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.3.31:kotlin-compiler-runner-1.3.31.jar:f8ab33e2ec54a1c62a189c0cab04fbadb58dfd1bdda6a8ade0849a7a9a598b7c',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.3.31:kotlin-daemon-client-1.3.31.jar:f658006ac301cae33e2a6cb1afd3cc41e82d98b12876de8fbe70a202434162de',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.3.31:kotlin-gradle-plugin-api-1.3.31.jar:e40152d09ec45eb9fd4c0a8340de46793ae3beeb0f70f8ab15dc0097767fc61c',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.3.31:kotlin-gradle-plugin-model-1.3.31.jar:9bbe7b3afebb43e81ef4e6a3202eb86d51dee34ddb305090d5cf0f2861ce87be',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31:kotlin-gradle-plugin-1.3.31.jar:307ced92080a1d7a887fd7f71eef7b297b514a205ecf947220bd7ce8391a5594',
|
||||
'org.jetbrains.kotlin:kotlin-native-utils:1.3.31:kotlin-native-utils-1.3.31.jar:00af02020516eed7942ace3811cacd9fa3b1de2b66c6498e17dbe3a3e9bacce1',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.3.0:kotlin-reflect-1.3.0.jar:f3231ac1c612fe72de6ffcc4f0b4c5d85ad1ad4c808fb01a1981eab1ee1202c3',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.3.21:kotlin-script-runtime-1.3.21.jar:2e25babc8dcd224b9c479e2c16ce7b4c50407d25f18d60d1fd262f78c2b474cb',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.21:kotlin-scripting-compiler-embeddable-1.3.21.jar:f4e6f9fd384d42167e9b89f985ee4a48a0676bfe705b2e2f9d13e1591d4b7c0b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.10:kotlin-stdlib-common-1.3.10.jar:7ebf12fdadc5fe80f7ed4dbeffb16618cee1c23cbff0b0489a254174500acc68',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.3.10:kotlin-stdlib-1.3.10.jar:9b9650550fac559f7db64d988123399ea3da7cb776bfb13b9a3ed818eef26969',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.3.31:kotlin-reflect-1.3.31.jar:a0172daf57e511e8e0df9251b508db8aa6b885cdf0c5849addc9b840db4814f0',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.3.31:kotlin-script-runtime-1.3.31.jar:633692186b292292e41ea60d5170e811845b78aba88e20260ba70f7ce3a3ef32',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.31:kotlin-scripting-compiler-embeddable-1.3.31.jar:4dff2f683f8ceee0e834aeb0ca2686774da6c010ad1faf671dcaf73f071de954',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31:kotlin-stdlib-common-1.3.31.jar:d6e9c54c1e6c4df21be9395de558665544c6bdc8f8076ea7518f089f82cd34fc',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31:kotlin-stdlib-jdk7-1.3.31.jar:dbf77e6a5626d941450fdc59cbfe24165858403c12789749a2497265269859a3',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.31:kotlin-stdlib-jdk8-1.3.31.jar:ad6acd219b468a532ac3b3c5aacbfd5db02d0ffcf967e2113e4677e2429490f6',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.3.31:kotlin-stdlib-1.3.31.jar:f38c84326543e66ed4895b20fb3ea0fca527fd5a040e1f49d0946ecf3d2b3b23',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.json:json:20150729:json-20150729.jar:38c21b9c3d6d24919cd15d027d20afab0a019ac9205f7ed9083b32bdd42a2353',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.3.1:junit-jupiter-api-5.3.1.jar:7923e21f030a9964d70a0e48007ca873280c66ddf0f0620b2d969852c23d5653',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.3.1:junit-jupiter-engine-5.3.1.jar:04f4354548a30827e126bdf6fcbe3640789ad8335a6f3f0762bf7f9f74e51fbf',
|
||||
'org.junit.jupiter:junit-jupiter-params:5.3.1:junit-jupiter-params-5.3.1.jar:72fe344712d4cd88dd0cb4bfa304322d512d2cb27173ed64cb5036a573d29f4c',
|
||||
'org.junit.platform:junit-platform-commons:1.3.1:junit-platform-commons-1.3.1.jar:457d8e1c0c80d1e320a792ec35e7c180694ba05182d7ecf7dabdb4e5a8a12fe2',
|
||||
'org.junit.platform:junit-platform-engine:1.3.1:junit-platform-engine-1.3.1.jar:303d0546c3e950cc3beaca00dfcbf2632d4eca530e4e446391bf193cbc2a71a3',
|
||||
'org.objenesis:objenesis:2.6:objenesis-2.6.jar:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.4.2:junit-jupiter-api-5.4.2.jar:cdfb355fee661633f15f2763b8c2029c2e1958585b97b9162d38a36b1754dc3e',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.4.2:junit-jupiter-engine-5.4.2.jar:42aead7c5c1b74e0ef775c374a9fc07c771fd61a3621e66df1793dba14e534fd',
|
||||
'org.junit.jupiter:junit-jupiter-params:5.4.2:junit-jupiter-params-5.4.2.jar:13f89bca59fb6931a0ca9e3f4dc74e1a3054e0c63863e091a5df4855605ae4ce',
|
||||
'org.junit.platform:junit-platform-commons:1.4.2:junit-platform-commons-1.4.2.jar:104bfa65b30ceb425a6de19d66b976caf38443ff5978ae931c103fa0f99d04ce',
|
||||
'org.junit.platform:junit-platform-engine:1.4.2:junit-platform-engine-1.4.2.jar:7edb2ad879a338a84dbb09202b1399640ec0cacc5a95168539a9a74b5a2302e1',
|
||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||
'org.opentest4j:opentest4j:1.1.1:opentest4j-1.1.1.jar:f106351abd941110226745ed103c85863b3f04e9fa82ddea1084639ae0c5336c',
|
||||
'org.skyscreamer:jsonassert:1.5.0:jsonassert-1.5.0.jar:a310bc79c3f4744e2b2e993702fcebaf3696fec0063643ffdc6b49a8fb03ef39',
|
||||
'org.slf4j:slf4j-api:1.7.25:slf4j-api-1.7.25.jar:18c4a0095d5c1da6b817592e767bb23d29dd2f560ad74df75ff3961dbde25b79',
|
||||
'org.slf4j:slf4j-simple:1.7.25:slf4j-simple-1.7.25.jar:0966e86fffa5be52d3d9e7b89dd674d98a03eed0a454fbaf7c1bd9493bd9d874',
|
||||
'org.slf4j:slf4j-api:1.7.26:slf4j-api-1.7.26.jar:6d9e5b86cfd1dd44c676899285b5bb4fa0d371cf583e8164f9c8a0366553242b',
|
||||
'org.slf4j:slf4j-simple:1.7.26:slf4j-simple-1.7.26.jar:4b8ed75e2273850bf4eeb411ae5de5e0c0a44da59a96ca68d284749a6a373678',
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user