Add /status and /setup mailbox API call with tests

This commit is contained in:
Torsten Grote
2021-12-13 16:06:03 -03:00
parent 65be2d2b26
commit d665fc17ec
8 changed files with 350 additions and 4 deletions

View File

@@ -0,0 +1,53 @@
package org.briarproject.bramble.mailbox;
import java.io.IOException;
import javax.annotation.concurrent.Immutable;
interface MailboxApi {
/**
* Sets up the mailbox with the setup token.
*
* @param properties MailboxProperties with the setup token
* @return the owner token
* @throws PermanentFailureException for 401 response.
*/
String setup(MailboxProperties properties)
throws IOException, PermanentFailureException;
/**
* Checks the status of the mailbox.
*
* @return true if the status is OK, false otherwise.
* @throws PermanentFailureException for 401 response.
*/
boolean checkStatus(MailboxProperties properties)
throws IOException, PermanentFailureException;
@Immutable
class MailboxProperties {
final String baseUrl;
final String token;
final boolean isOwner;
MailboxProperties(String baseUrl, String token, boolean isOwner) {
this.baseUrl = baseUrl;
this.token = token;
this.isOwner = isOwner;
}
}
@Immutable
class PermanentFailureException extends Exception {
/**
* If true, the failure is fatal and requires user attention.
* The entire task queue will most likely need to stop.
*/
final boolean fatal;
PermanentFailureException(boolean fatal) {
this.fatal = fatal;
}
}
}

View File

@@ -0,0 +1,87 @@
package org.briarproject.bramble.mailbox;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import javax.inject.Inject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
import static okhttp3.internal.Util.EMPTY_REQUEST;
@NotNullByDefault
class MailboxApiImpl implements MailboxApi {
private final WeakSingletonProvider<OkHttpClient> httpClientProvider;
private final JsonMapper mapper = JsonMapper.builder()
.enable(BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES)
.build();
@Inject
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider) {
this.httpClientProvider = httpClientProvider;
}
@Override
public String setup(MailboxProperties properties)
throws IOException, PermanentFailureException {
if (!properties.isOwner) throw new IllegalArgumentException();
Request request = getRequestBuilder(properties.token)
.url(properties.baseUrl + "/setup")
.put(EMPTY_REQUEST)
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() == 401) {
throw new PermanentFailureException(true);
}
if (!response.isSuccessful()) throw new IOException();
ResponseBody body = response.body();
if (body == null) throw new PermanentFailureException(false);
try {
JsonNode node = mapper.readTree(body.string());
JsonNode tokenNode = node.get("token");
if (tokenNode == null) {
throw new PermanentFailureException(false);
}
String ownerToken = tokenNode.textValue();
if (ownerToken == null) {
throw new PermanentFailureException(false);
}
return ownerToken;
} catch (JacksonException e) {
throw new PermanentFailureException(false);
}
}
@Override
public boolean checkStatus(MailboxProperties properties)
throws IOException, PermanentFailureException {
if (!properties.isOwner) throw new IllegalArgumentException();
Request request = getRequestBuilder(properties.token)
.url(properties.baseUrl + "/status")
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() == 401) {
throw new PermanentFailureException(true);
}
return response.isSuccessful();
}
private Request.Builder getRequestBuilder(String token) {
return new Request.Builder()
.addHeader("Authorization", "Bearer " + token);
}
}

View File

@@ -0,0 +1,193 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxProperties;
import org.briarproject.bramble.mailbox.MailboxApi.PermanentFailureException;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.net.SocketFactory;
import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class MailboxApiTest extends BrambleTestCase {
private final OkHttpClient client = new OkHttpClient.Builder()
.socketFactory(SocketFactory.getDefault())
.connectTimeout(60_000, MILLISECONDS)
.build();
private final WeakSingletonProvider<OkHttpClient> httpClientProvider =
new WeakSingletonProvider<OkHttpClient>() {
@Override
@Nonnull
public OkHttpClient createInstance() {
return client;
}
};
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider);
private final String token = getRandomString(64);
private final String token2 = getRandomString(64);
@Test
public void testSetup() throws Exception {
String validResponse = "{\"token\":\"" + token2 + "\"}";
String invalidResponse = "{\"foo\":\"bar\"}";
String invalidTokenResponse = "{\"token\":{\"foo\":\"bar\"}}";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(validResponse));
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setBody(invalidResponse));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.enqueue(new MockResponse().setBody(invalidTokenResponse));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
MailboxProperties properties2 =
new MailboxProperties(baseUrl, token2, true);
assertEquals(token2, api.setup(properties));
PermanentFailureException e2 =
assertThrows(PermanentFailureException.class,
() -> api.setup(properties)
);
PermanentFailureException e3 =
assertThrows(PermanentFailureException.class,
() -> api.setup(properties)
);
PermanentFailureException e4 = assertThrows(
PermanentFailureException.class,
() -> api.setup(properties2)
);
assertThrows(IOException.class,
() -> api.setup(properties)
);
PermanentFailureException e6 = assertThrows(
PermanentFailureException.class,
() -> api.setup(properties)
);
RecordedRequest request1 = server.takeRequest();
assertEquals("/setup", request1.getPath());
assertEquals("PUT", request1.getMethod());
assertToken(request1, token);
RecordedRequest request2 = server.takeRequest();
assertEquals("/setup", request2.getPath());
assertEquals("PUT", request2.getMethod());
assertToken(request2, token);
assertFalse(e2.fatal);
RecordedRequest request3 = server.takeRequest();
assertEquals("/setup", request3.getPath());
assertEquals("PUT", request3.getMethod());
assertToken(request3, token);
assertFalse(e3.fatal);
RecordedRequest request4 = server.takeRequest();
assertEquals("/setup", request4.getPath());
assertEquals("PUT", request4.getMethod());
assertToken(request4, token2);
assertTrue(e4.fatal);
RecordedRequest request5 = server.takeRequest();
assertEquals("/setup", request5.getPath());
assertEquals("PUT", request5.getMethod());
assertToken(request5, token);
RecordedRequest request6 = server.takeRequest();
assertEquals("/setup", request6.getPath());
assertEquals("PUT", request6.getMethod());
assertToken(request6, token);
assertFalse(e6.fatal);
}
@Test
public void testSetupOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(
IllegalArgumentException.class,
() -> api.setup(properties)
);
}
@Test
public void testStatus() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
MailboxProperties properties2 =
new MailboxProperties(baseUrl, token2, true);
assertTrue(api.checkStatus(properties));
PermanentFailureException e2 = assertThrows(
PermanentFailureException.class,
() -> api.checkStatus(properties2)
);
assertFalse(api.checkStatus(properties));
RecordedRequest request1 = server.takeRequest();
assertEquals("/status", request1.getPath());
assertToken(request1, token);
RecordedRequest request2 = server.takeRequest();
assertEquals("/status", request2.getPath());
assertToken(request2, token2);
assertTrue(e2.fatal);
RecordedRequest request3 = server.takeRequest();
assertEquals("/status", request3.getPath());
assertToken(request3, token);
}
@Test
public void testStatusOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(
IllegalArgumentException.class,
() -> api.checkStatus(properties)
);
}
private String getBaseUrl(MockWebServer server) {
String baseUrl = server.url("").toString();
return baseUrl.substring(0, baseUrl.length() - 1);
}
private void assertToken(RecordedRequest request, String token) {
assertNotNull(request.getHeader("Authorization"));
assertEquals("Bearer " + token, request.getHeader("Authorization"));
}
}