mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Merge branch '2231-file-api' into 'master'
Add Mailbox File Mangement API Closes #2233, #2232, and #2231 See merge request briar/briar!1581
This commit is contained in:
@@ -9,6 +9,7 @@ apply from: 'witness.gradle'
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "com.google.dagger:dagger:$dagger_version"
|
implementation "com.google.dagger:dagger:$dagger_version"
|
||||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class InvalidMailboxIdException extends Exception {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MailboxAuthToken extends MailboxId {
|
||||||
|
public MailboxAuthToken(byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MailboxAuthToken} from the given string.
|
||||||
|
*
|
||||||
|
* @throws InvalidMailboxIdException if token is not valid.
|
||||||
|
*/
|
||||||
|
public static MailboxAuthToken fromString(@Nullable String token)
|
||||||
|
throws InvalidMailboxIdException {
|
||||||
|
return new MailboxAuthToken(bytesFromString(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MailboxFileId extends MailboxId {
|
||||||
|
public MailboxFileId(byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MailboxFileId} from the given string.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if token is not valid.
|
||||||
|
*/
|
||||||
|
public static MailboxFileId fromString(@Nullable String token)
|
||||||
|
throws InvalidMailboxIdException {
|
||||||
|
return new MailboxFileId(bytesFromString(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MailboxFolderId extends MailboxId {
|
||||||
|
public MailboxFolderId(byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MailboxFolderId} from the given string.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if token is not valid.
|
||||||
|
*/
|
||||||
|
public static MailboxFolderId fromString(@Nullable String token)
|
||||||
|
throws InvalidMailboxIdException {
|
||||||
|
return new MailboxFolderId(bytesFromString(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
public abstract class MailboxId extends UniqueId {
|
||||||
|
MailboxId(byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns valid {@link MailboxId} bytes from the given string.
|
||||||
|
*
|
||||||
|
* @throws InvalidMailboxIdException if token is not valid.
|
||||||
|
*/
|
||||||
|
static byte[] bytesFromString(@Nullable String token)
|
||||||
|
throws InvalidMailboxIdException {
|
||||||
|
if (token == null || token.length() != 64) {
|
||||||
|
throw new InvalidMailboxIdException();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return fromHexString(token);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new InvalidMailboxIdException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string representation expected by the mailbox API.
|
||||||
|
* Also used for serialization.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@JsonValue
|
||||||
|
public String toString() {
|
||||||
|
return toHexString(getBytes()).toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,10 +8,11 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class MailboxProperties {
|
public class MailboxProperties {
|
||||||
|
|
||||||
private final String onionAddress, authToken;
|
private final String onionAddress;
|
||||||
|
private final MailboxAuthToken authToken;
|
||||||
private final boolean owner;
|
private final boolean owner;
|
||||||
|
|
||||||
public MailboxProperties(String onionAddress, String authToken,
|
public MailboxProperties(String onionAddress, MailboxAuthToken authToken,
|
||||||
boolean owner) {
|
boolean owner) {
|
||||||
this.onionAddress = onionAddress;
|
this.onionAddress = onionAddress;
|
||||||
this.authToken = authToken;
|
this.authToken = authToken;
|
||||||
@@ -22,7 +23,7 @@ public class MailboxProperties {
|
|||||||
return onionAddress;
|
return onionAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthToken() {
|
public MailboxAuthToken getAuthToken() {
|
||||||
return authToken;
|
return authToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,16 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@@ -46,8 +49,8 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
|
|||||||
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
|
||||||
|
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
|
|
||||||
@@ -211,8 +214,22 @@ public class TestUtils {
|
|||||||
getAgreementPublicKey(), verified);
|
getAgreementPublicKey(), verified);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMailboxSecret() {
|
public static void writeBytes(File file, byte[] bytes)
|
||||||
return toHexString(getRandomBytes(32)).toLowerCase(Locale.US);
|
throws IOException {
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(file);
|
||||||
|
//noinspection TryFinallyCanBeTryWithResources
|
||||||
|
try {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
} finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(File file) throws IOException {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
FileInputStream inputStream = new FileInputStream(file);
|
||||||
|
copyAndClose(inputStream, outputStream);
|
||||||
|
return outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getMedian(Collection<? extends Number> samples) {
|
public static double getMedian(Collection<? extends Number> samples) {
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ package org.briarproject.bramble.mailbox;
|
|||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
interface MailboxApi {
|
interface MailboxApi {
|
||||||
@@ -19,7 +25,7 @@ interface MailboxApi {
|
|||||||
* @return the owner token
|
* @return the owner token
|
||||||
* @throws ApiException for 401 response.
|
* @throws ApiException for 401 response.
|
||||||
*/
|
*/
|
||||||
String setup(MailboxProperties properties)
|
MailboxAuthToken setup(MailboxProperties properties)
|
||||||
throws IOException, ApiException;
|
throws IOException, ApiException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,16 +63,69 @@ interface MailboxApi {
|
|||||||
Collection<ContactId> getContacts(MailboxProperties properties)
|
Collection<ContactId> getContacts(MailboxProperties properties)
|
||||||
throws IOException, ApiException;
|
throws IOException, ApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by contacts to send files to the owner
|
||||||
|
* and by the owner to send files to contacts.
|
||||||
|
* <p>
|
||||||
|
* The owner can add files to the contacts' inboxes
|
||||||
|
* and the contacts can add files to their own outbox.
|
||||||
|
*/
|
||||||
|
void addFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||||
|
File file) throws IOException, ApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by owner and contacts to list their files to retrieve.
|
||||||
|
* <p>
|
||||||
|
* Returns 200 OK with the list of files in JSON.
|
||||||
|
*/
|
||||||
|
List<MailboxFile> getFiles(MailboxProperties properties,
|
||||||
|
MailboxFolderId folderId) throws IOException, ApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by owner and contacts to retrieve a file.
|
||||||
|
* <p>
|
||||||
|
* Returns 200 OK if successful with the files' raw bytes
|
||||||
|
* in the response body.
|
||||||
|
*
|
||||||
|
* @param file the empty file the response bytes will be written into.
|
||||||
|
*/
|
||||||
|
void getFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||||
|
MailboxFileId fileId, File file) throws IOException, ApiException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by owner and contacts to delete files.
|
||||||
|
* <p>
|
||||||
|
* Returns 200 OK (no exception) if deletion was successful.
|
||||||
|
*
|
||||||
|
* @throws TolerableFailureException on 404 response,
|
||||||
|
* because file was most likely deleted already.
|
||||||
|
*/
|
||||||
|
void deleteFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||||
|
MailboxFileId fileId)
|
||||||
|
throws IOException, ApiException, TolerableFailureException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all contact outboxes that have files available
|
||||||
|
* for the owner to download.
|
||||||
|
*
|
||||||
|
* @return a list of folder names
|
||||||
|
* to be used with {@link #getFiles(MailboxProperties, MailboxFolderId)}.
|
||||||
|
* @throws IllegalArgumentException if used by non-owner.
|
||||||
|
*/
|
||||||
|
List<MailboxFolderId> getFolders(MailboxProperties properties)
|
||||||
|
throws IOException, ApiException;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@JsonSerialize
|
@JsonSerialize
|
||||||
class MailboxContact {
|
class MailboxContact {
|
||||||
public final int contactId;
|
public final int contactId;
|
||||||
public final String token, inboxId, outboxId;
|
public final MailboxAuthToken token;
|
||||||
|
public final MailboxFolderId inboxId, outboxId;
|
||||||
|
|
||||||
MailboxContact(ContactId contactId,
|
MailboxContact(ContactId contactId,
|
||||||
String token,
|
MailboxAuthToken token,
|
||||||
String inboxId,
|
MailboxFolderId inboxId,
|
||||||
String outboxId) {
|
MailboxFolderId outboxId) {
|
||||||
this.contactId = contactId.getInt();
|
this.contactId = contactId.getInt();
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.inboxId = inboxId;
|
this.inboxId = inboxId;
|
||||||
@@ -74,6 +133,24 @@ interface MailboxApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerialize
|
||||||
|
class MailboxFile implements Comparable<MailboxFile> {
|
||||||
|
public final MailboxFileId name;
|
||||||
|
public final long time;
|
||||||
|
|
||||||
|
public MailboxFile(MailboxFileId name, long time) {
|
||||||
|
this.name = name;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@Nonnull MailboxApi.MailboxFile mailboxFile) {
|
||||||
|
//noinspection UseCompareMethod
|
||||||
|
return time < mailboxFile.time ? -1 :
|
||||||
|
(time == mailboxFile.time ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class ApiException extends Exception {
|
class ApiException extends Exception {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,25 @@ package org.briarproject.bramble.mailbox;
|
|||||||
import com.fasterxml.jackson.core.JacksonException;
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -26,7 +36,7 @@ import okhttp3.ResponseBody;
|
|||||||
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
|
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static okhttp3.internal.Util.EMPTY_REQUEST;
|
import static okhttp3.internal.Util.EMPTY_REQUEST;
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class MailboxApiImpl implements MailboxApi {
|
class MailboxApiImpl implements MailboxApi {
|
||||||
@@ -37,6 +47,8 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
.build();
|
.build();
|
||||||
private static final MediaType JSON =
|
private static final MediaType JSON =
|
||||||
requireNonNull(MediaType.parse("application/json; charset=utf-8"));
|
requireNonNull(MediaType.parse("application/json; charset=utf-8"));
|
||||||
|
private static final MediaType FILE =
|
||||||
|
requireNonNull(MediaType.parse("application/octet-stream"));
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider) {
|
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider) {
|
||||||
@@ -44,7 +56,7 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String setup(MailboxProperties properties)
|
public MailboxAuthToken setup(MailboxProperties properties)
|
||||||
throws IOException, ApiException {
|
throws IOException, ApiException {
|
||||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||||
Request request = getRequestBuilder(properties.getAuthToken())
|
Request request = getRequestBuilder(properties.getAuthToken())
|
||||||
@@ -65,26 +77,12 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
throw new ApiException();
|
throw new ApiException();
|
||||||
}
|
}
|
||||||
String ownerToken = tokenNode.textValue();
|
String ownerToken = tokenNode.textValue();
|
||||||
if (ownerToken == null || !isValidToken(ownerToken)) {
|
return MailboxAuthToken.fromString(ownerToken);
|
||||||
throw new ApiException();
|
} catch (JacksonException | InvalidMailboxIdException e) {
|
||||||
}
|
|
||||||
return ownerToken;
|
|
||||||
} catch (JacksonException e) {
|
|
||||||
throw new ApiException();
|
throw new ApiException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidToken(String token) {
|
|
||||||
if (token.length() != 64) return false;
|
|
||||||
try {
|
|
||||||
// try to convert to bytes
|
|
||||||
fromHexString(token);
|
|
||||||
return true;
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkStatus(MailboxProperties properties)
|
public boolean checkStatus(MailboxProperties properties)
|
||||||
throws IOException, ApiException {
|
throws IOException, ApiException {
|
||||||
@@ -94,19 +92,15 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
return response.isSuccessful();
|
return response.isSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Contact Management API (owner only) */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addContact(MailboxProperties properties, MailboxContact contact)
|
public void addContact(MailboxProperties properties, MailboxContact contact)
|
||||||
throws IOException, ApiException,
|
throws IOException, ApiException, TolerableFailureException {
|
||||||
TolerableFailureException {
|
|
||||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||||
byte[] bodyBytes = mapper.writeValueAsBytes(contact);
|
byte[] bodyBytes = mapper.writeValueAsBytes(contact);
|
||||||
RequestBody body = RequestBody.create(JSON, bodyBytes);
|
RequestBody body = RequestBody.create(JSON, bodyBytes);
|
||||||
Request request = getRequestBuilder(properties.getAuthToken())
|
Response response = sendPostRequest(properties, "/contacts", body);
|
||||||
.url(properties.getOnionAddress() + "/contacts")
|
|
||||||
.post(body)
|
|
||||||
.build();
|
|
||||||
OkHttpClient client = httpClientProvider.get();
|
|
||||||
Response response = client.newCall(request).execute();
|
|
||||||
if (response.code() == 409) throw new TolerableFailureException();
|
if (response.code() == 409) throw new TolerableFailureException();
|
||||||
if (!response.isSuccessful()) throw new ApiException();
|
if (!response.isSuccessful()) throw new ApiException();
|
||||||
}
|
}
|
||||||
@@ -138,10 +132,7 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
if (body == null) throw new ApiException();
|
if (body == null) throw new ApiException();
|
||||||
try {
|
try {
|
||||||
JsonNode node = mapper.readTree(body.string());
|
JsonNode node = mapper.readTree(body.string());
|
||||||
JsonNode contactsNode = node.get("contacts");
|
ArrayNode contactsNode = getArray(node, "contacts");
|
||||||
if (contactsNode == null || !contactsNode.isArray()) {
|
|
||||||
throw new ApiException();
|
|
||||||
}
|
|
||||||
List<ContactId> list = new ArrayList<>();
|
List<ContactId> list = new ArrayList<>();
|
||||||
for (JsonNode contactNode : contactsNode) {
|
for (JsonNode contactNode : contactsNode) {
|
||||||
if (!contactNode.isNumber()) throw new ApiException();
|
if (!contactNode.isNumber()) throw new ApiException();
|
||||||
@@ -155,6 +146,112 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* File Management (owner and contacts) */
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||||
|
File file) throws IOException, ApiException {
|
||||||
|
String path = "/files/" + folderId;
|
||||||
|
RequestBody body = RequestBody.create(FILE, file);
|
||||||
|
Response response = sendPostRequest(properties, path, body);
|
||||||
|
if (response.code() != 200) throw new ApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MailboxFile> getFiles(MailboxProperties properties,
|
||||||
|
MailboxFolderId folderId) throws IOException, ApiException {
|
||||||
|
String path = "/files/" + folderId;
|
||||||
|
Response response = sendGetRequest(properties, path);
|
||||||
|
if (response.code() != 200) throw new ApiException();
|
||||||
|
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
if (body == null) throw new ApiException();
|
||||||
|
try {
|
||||||
|
JsonNode node = mapper.readTree(body.string());
|
||||||
|
ArrayNode filesNode = getArray(node, "files");
|
||||||
|
List<MailboxFile> list = new ArrayList<>();
|
||||||
|
for (JsonNode fileNode : filesNode) {
|
||||||
|
if (!fileNode.isObject()) throw new ApiException();
|
||||||
|
ObjectNode objectNode = (ObjectNode) fileNode;
|
||||||
|
JsonNode nameNode = objectNode.get("name");
|
||||||
|
JsonNode timeNode = objectNode.get("time");
|
||||||
|
if (nameNode == null || !nameNode.isTextual()) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
if (timeNode == null || !timeNode.isNumber()) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
String name = nameNode.asText();
|
||||||
|
long time = timeNode.asLong();
|
||||||
|
if (time < 1) throw new ApiException();
|
||||||
|
list.add(new MailboxFile(MailboxFileId.fromString(name), time));
|
||||||
|
}
|
||||||
|
Collections.sort(list);
|
||||||
|
return list;
|
||||||
|
} catch (JacksonException | InvalidMailboxIdException e) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||||
|
MailboxFileId fileId, File file) throws IOException, ApiException {
|
||||||
|
String path = "/files/" + folderId + "/" + fileId;
|
||||||
|
Response response = sendGetRequest(properties, path);
|
||||||
|
if (response.code() != 200) throw new ApiException();
|
||||||
|
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
if (body == null) throw new ApiException();
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(file);
|
||||||
|
copyAndClose(body.byteStream(), outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteFile(MailboxProperties properties,
|
||||||
|
MailboxFolderId folderId, MailboxFileId fileId)
|
||||||
|
throws IOException, ApiException, TolerableFailureException {
|
||||||
|
String path = "/files/" + folderId + "/" + fileId;
|
||||||
|
Request request = getRequestBuilder(properties.getAuthToken())
|
||||||
|
.delete()
|
||||||
|
.url(properties.getOnionAddress() + path)
|
||||||
|
.build();
|
||||||
|
OkHttpClient client = httpClientProvider.get();
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
|
if (response.code() == 404) throw new TolerableFailureException();
|
||||||
|
if (response.code() != 200) throw new ApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MailboxFolderId> getFolders(MailboxProperties properties)
|
||||||
|
throws IOException, ApiException {
|
||||||
|
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||||
|
Response response = sendGetRequest(properties, "/folders");
|
||||||
|
if (response.code() != 200) throw new ApiException();
|
||||||
|
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
if (body == null) throw new ApiException();
|
||||||
|
try {
|
||||||
|
JsonNode node = mapper.readTree(body.string());
|
||||||
|
ArrayNode filesNode = getArray(node, "folders");
|
||||||
|
List<MailboxFolderId> list = new ArrayList<>();
|
||||||
|
for (JsonNode fileNode : filesNode) {
|
||||||
|
if (!fileNode.isObject()) throw new ApiException();
|
||||||
|
ObjectNode objectNode = (ObjectNode) fileNode;
|
||||||
|
JsonNode idNode = objectNode.get("id");
|
||||||
|
if (idNode == null || !idNode.isTextual()) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
String id = idNode.asText();
|
||||||
|
list.add(MailboxFolderId.fromString(id));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} catch (JacksonException | InvalidMailboxIdException e) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper Functions */
|
||||||
|
|
||||||
private Response sendGetRequest(MailboxProperties properties, String path)
|
private Response sendGetRequest(MailboxProperties properties, String path)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Request request = getRequestBuilder(properties.getAuthToken())
|
Request request = getRequestBuilder(properties.getAuthToken())
|
||||||
@@ -164,9 +261,29 @@ class MailboxApiImpl implements MailboxApi {
|
|||||||
return client.newCall(request).execute();
|
return client.newCall(request).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Request.Builder getRequestBuilder(String token) {
|
private Response sendPostRequest(MailboxProperties properties, String path,
|
||||||
|
RequestBody body) throws IOException {
|
||||||
|
Request request = getRequestBuilder(properties.getAuthToken())
|
||||||
|
.url(properties.getOnionAddress() + path)
|
||||||
|
.post(body)
|
||||||
|
.build();
|
||||||
|
OkHttpClient client = httpClientProvider.get();
|
||||||
|
return client.newCall(request).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Request.Builder getRequestBuilder(MailboxId token) {
|
||||||
return new Request.Builder()
|
return new Request.Builder()
|
||||||
.addHeader("Authorization", "Bearer " + token);
|
.addHeader("Authorization", "Bearer " + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* JSON helpers */
|
||||||
|
|
||||||
|
private ArrayNode getArray(JsonNode node, String name) throws ApiException {
|
||||||
|
JsonNode arrayNode = node.get(name);
|
||||||
|
if (arrayNode == null || !arrayNode.isArray()) {
|
||||||
|
throw new ApiException();
|
||||||
|
}
|
||||||
|
return (ArrayNode) arrayNode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.mailbox;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||||
@@ -43,7 +45,12 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
String onion = s.get(SETTINGS_KEY_ONION);
|
String onion = s.get(SETTINGS_KEY_ONION);
|
||||||
String token = s.get(SETTINGS_KEY_TOKEN);
|
String token = s.get(SETTINGS_KEY_TOKEN);
|
||||||
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
|
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
|
||||||
return new MailboxProperties(onion, token, true);
|
try {
|
||||||
|
MailboxAuthToken tokenId = MailboxAuthToken.fromString(token);
|
||||||
|
return new MailboxProperties(onion, tokenId, true);
|
||||||
|
} catch (InvalidMailboxIdException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,7 +58,7 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
throws DbException {
|
throws DbException {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put(SETTINGS_KEY_ONION, p.getOnionAddress());
|
s.put(SETTINGS_KEY_ONION, p.getOnionAddress());
|
||||||
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken());
|
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken().toString());
|
||||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,26 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@@ -19,12 +30,17 @@ import okhttp3.OkHttpClient;
|
|||||||
import okhttp3.mockwebserver.MockResponse;
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
import okhttp3.mockwebserver.MockWebServer;
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
import okhttp3.mockwebserver.RecordedRequest;
|
import okhttp3.mockwebserver.RecordedRequest;
|
||||||
|
import okio.Buffer;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxSecret;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.readBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.writeBytes;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -33,6 +49,9 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public class MailboxApiTest extends BrambleTestCase {
|
public class MailboxApiTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder folder = new TemporaryFolder();
|
||||||
|
|
||||||
private final OkHttpClient client = new OkHttpClient.Builder()
|
private final OkHttpClient client = new OkHttpClient.Builder()
|
||||||
.socketFactory(SocketFactory.getDefault())
|
.socketFactory(SocketFactory.getDefault())
|
||||||
.connectTimeout(60_000, MILLISECONDS)
|
.connectTimeout(60_000, MILLISECONDS)
|
||||||
@@ -47,12 +66,15 @@ public class MailboxApiTest extends BrambleTestCase {
|
|||||||
};
|
};
|
||||||
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider);
|
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider);
|
||||||
|
|
||||||
private final String token = getMailboxSecret();
|
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
|
||||||
private final String token2 = getMailboxSecret();
|
private final MailboxAuthToken token2 = new MailboxAuthToken(getRandomId());
|
||||||
private final ContactId contactId = getContactId();
|
private final ContactId contactId = getContactId();
|
||||||
private final String contactToken = getMailboxSecret();
|
private final MailboxAuthToken contactToken =
|
||||||
private final String contactInboxId = getMailboxSecret();
|
new MailboxAuthToken(getRandomId());
|
||||||
private final String contactOutboxId = getMailboxSecret();
|
private final MailboxFolderId contactInboxId =
|
||||||
|
new MailboxFolderId(getRandomId());
|
||||||
|
private final MailboxFolderId contactOutboxId =
|
||||||
|
new MailboxFolderId(getRandomId());
|
||||||
private final MailboxContact mailboxContact = new MailboxContact(
|
private final MailboxContact mailboxContact = new MailboxContact(
|
||||||
contactId, contactToken, contactInboxId, contactOutboxId);
|
contactId, contactToken, contactInboxId, contactOutboxId);
|
||||||
|
|
||||||
@@ -370,12 +392,362 @@ public class MailboxApiTest extends BrambleTestCase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddFile() throws Exception {
|
||||||
|
File file = folder.newFile();
|
||||||
|
byte[] bytes = getRandomBytes(1337);
|
||||||
|
writeBytes(file, bytes);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// file gets uploaded as expected
|
||||||
|
api.addFile(properties, contactInboxId, file);
|
||||||
|
RecordedRequest request1 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request1.getPath());
|
||||||
|
assertEquals("POST", request1.getMethod());
|
||||||
|
assertToken(request1, token);
|
||||||
|
assertArrayEquals(bytes, request1.getBody().readByteArray());
|
||||||
|
|
||||||
|
// request is not successful
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.addFile(properties, contactInboxId, file));
|
||||||
|
RecordedRequest request2 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request2.getPath());
|
||||||
|
assertEquals("POST", request1.getMethod());
|
||||||
|
assertToken(request2, token);
|
||||||
|
|
||||||
|
// server error
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.addFile(properties, contactInboxId, file));
|
||||||
|
RecordedRequest request3 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request3.getPath());
|
||||||
|
assertEquals("POST", request1.getMethod());
|
||||||
|
assertToken(request3, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFiles() throws Exception {
|
||||||
|
MailboxFile mailboxFile1 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()), 1337);
|
||||||
|
MailboxFile mailboxFile2 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()),
|
||||||
|
System.currentTimeMillis());
|
||||||
|
String fileResponse1 =
|
||||||
|
new ObjectMapper().writeValueAsString(mailboxFile1);
|
||||||
|
String fileResponse2 =
|
||||||
|
new ObjectMapper().writeValueAsString(mailboxFile2);
|
||||||
|
String validResponse1 = "{\"files\": [" + fileResponse1 + "] }";
|
||||||
|
String validResponse2 = "{\"files\": [" + fileResponse1 + ", " +
|
||||||
|
fileResponse2 + "] }";
|
||||||
|
String invalidResponse1 = "{\"files\":\"bar\"}";
|
||||||
|
String invalidResponse2 = "{\"files\":{\"foo\":\"bar\"}}";
|
||||||
|
String invalidResponse3 = "{\"files\": [" + fileResponse1 + ", 1] }";
|
||||||
|
String invalidResponse4 = "{\"contacts\": [ 1, 2 ] }";
|
||||||
|
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
server.enqueue(new MockResponse().setBody(validResponse1));
|
||||||
|
server.enqueue(new MockResponse().setBody(validResponse2));
|
||||||
|
server.enqueue(new MockResponse());
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse1));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse2));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse3));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse4));
|
||||||
|
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);
|
||||||
|
|
||||||
|
// valid response with one file
|
||||||
|
List<MailboxFile> received1 = api.getFiles(properties, contactInboxId);
|
||||||
|
assertEquals(1, received1.size());
|
||||||
|
assertEquals(mailboxFile1.name, received1.get(0).name);
|
||||||
|
assertEquals(mailboxFile1.time, received1.get(0).time);
|
||||||
|
RecordedRequest request1 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request1.getPath());
|
||||||
|
assertEquals("GET", request1.getMethod());
|
||||||
|
assertToken(request1, token);
|
||||||
|
|
||||||
|
// valid response with two files
|
||||||
|
List<MailboxFile> received2 = api.getFiles(properties, contactInboxId);
|
||||||
|
assertEquals(2, received2.size());
|
||||||
|
assertEquals(mailboxFile1.name, received2.get(0).name);
|
||||||
|
assertEquals(mailboxFile1.time, received2.get(0).time);
|
||||||
|
assertEquals(mailboxFile2.name, received2.get(1).name);
|
||||||
|
assertEquals(mailboxFile2.time, received2.get(1).time);
|
||||||
|
RecordedRequest request2 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request1.getPath());
|
||||||
|
assertEquals("GET", request2.getMethod());
|
||||||
|
assertToken(request2, token);
|
||||||
|
|
||||||
|
// empty body
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request3 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request3.getPath());
|
||||||
|
assertEquals("GET", request3.getMethod());
|
||||||
|
assertToken(request3, token);
|
||||||
|
|
||||||
|
// invalid response: string instead of list
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request4 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request4.getPath());
|
||||||
|
assertEquals("GET", request4.getMethod());
|
||||||
|
assertToken(request4, token);
|
||||||
|
|
||||||
|
// invalid response: object instead of list
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request5 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request5.getPath());
|
||||||
|
assertEquals("GET", request5.getMethod());
|
||||||
|
assertToken(request5, token);
|
||||||
|
|
||||||
|
// invalid response: list with non-objects
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request6 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request6.getPath());
|
||||||
|
assertEquals("GET", request6.getMethod());
|
||||||
|
assertToken(request6, token);
|
||||||
|
|
||||||
|
// no files key in root object
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request7 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request7.getPath());
|
||||||
|
assertEquals("GET", request7.getMethod());
|
||||||
|
assertToken(request7, token);
|
||||||
|
|
||||||
|
// 401 not authorized
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request8 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request8.getPath());
|
||||||
|
assertEquals("GET", request8.getMethod());
|
||||||
|
assertToken(request8, token);
|
||||||
|
|
||||||
|
// 500 internal server error
|
||||||
|
assertThrows(ApiException.class,
|
||||||
|
() -> api.getFiles(properties, contactInboxId));
|
||||||
|
RecordedRequest request9 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactInboxId, request9.getPath());
|
||||||
|
assertEquals("GET", request9.getMethod());
|
||||||
|
assertToken(request9, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFile() throws Exception {
|
||||||
|
MailboxFileId name = new MailboxFileId(getRandomId());
|
||||||
|
File file1 = folder.newFile();
|
||||||
|
File file2 = folder.newFile();
|
||||||
|
File file3 = folder.newFile();
|
||||||
|
byte[] bytes = getRandomBytes(1337);
|
||||||
|
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
server.enqueue(new MockResponse().setBody(new Buffer().write(bytes)));
|
||||||
|
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);
|
||||||
|
|
||||||
|
// file gets downloaded as expected
|
||||||
|
api.getFile(properties, contactOutboxId, name, file1);
|
||||||
|
RecordedRequest request1 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactOutboxId + "/" + name,
|
||||||
|
request1.getPath());
|
||||||
|
assertEquals("GET", request1.getMethod());
|
||||||
|
assertToken(request1, token);
|
||||||
|
assertArrayEquals(bytes, readBytes(file1));
|
||||||
|
|
||||||
|
// request is not successful
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFile(properties, contactOutboxId, name, file2));
|
||||||
|
RecordedRequest request2 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactOutboxId + "/" + name,
|
||||||
|
request2.getPath());
|
||||||
|
assertEquals("GET", request1.getMethod());
|
||||||
|
assertToken(request2, token);
|
||||||
|
assertEquals(0, readBytes(file2).length);
|
||||||
|
|
||||||
|
// server error
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFile(properties, contactOutboxId, name, file3));
|
||||||
|
RecordedRequest request3 = server.takeRequest();
|
||||||
|
assertEquals("/files/" + contactOutboxId + "/" + name,
|
||||||
|
request3.getPath());
|
||||||
|
assertEquals("GET", request1.getMethod());
|
||||||
|
assertToken(request3, token);
|
||||||
|
assertEquals(0, readBytes(file3).length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteFile() throws Exception {
|
||||||
|
MailboxFileId name = new MailboxFileId(getRandomId());
|
||||||
|
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
server.enqueue(new MockResponse());
|
||||||
|
server.enqueue(new MockResponse().setResponseCode(205));
|
||||||
|
server.enqueue(new MockResponse().setResponseCode(401));
|
||||||
|
server.enqueue(new MockResponse().setResponseCode(404));
|
||||||
|
server.start();
|
||||||
|
String baseUrl = getBaseUrl(server);
|
||||||
|
MailboxProperties properties =
|
||||||
|
new MailboxProperties(baseUrl, token, true);
|
||||||
|
|
||||||
|
// file gets deleted as expected
|
||||||
|
api.deleteFile(properties, contactInboxId, name);
|
||||||
|
RecordedRequest request1 = server.takeRequest();
|
||||||
|
assertEquals("DELETE", request1.getMethod());
|
||||||
|
assertEquals("/files/" + contactInboxId + "/" + name,
|
||||||
|
request1.getPath());
|
||||||
|
assertToken(request1, token);
|
||||||
|
|
||||||
|
// request is not returning 200
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.deleteFile(properties, contactInboxId, name));
|
||||||
|
RecordedRequest request2 = server.takeRequest();
|
||||||
|
assertEquals("DELETE", request2.getMethod());
|
||||||
|
assertEquals("/files/" + contactInboxId + "/" + name,
|
||||||
|
request2.getPath());
|
||||||
|
assertToken(request2, token);
|
||||||
|
|
||||||
|
// request is not authorized
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.deleteFile(properties, contactInboxId, name));
|
||||||
|
RecordedRequest request3 = server.takeRequest();
|
||||||
|
assertEquals("DELETE", request3.getMethod());
|
||||||
|
assertEquals("/files/" + contactInboxId + "/" + name,
|
||||||
|
request3.getPath());
|
||||||
|
assertToken(request3, token);
|
||||||
|
|
||||||
|
// file not found is tolerable
|
||||||
|
assertThrows(TolerableFailureException.class, () ->
|
||||||
|
api.deleteFile(properties, contactInboxId, name));
|
||||||
|
RecordedRequest request4 = server.takeRequest();
|
||||||
|
assertEquals("DELETE", request4.getMethod());
|
||||||
|
assertEquals("/files/" + contactInboxId + "/" + name,
|
||||||
|
request4.getPath());
|
||||||
|
assertToken(request4, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFolders() throws Exception {
|
||||||
|
MailboxFolderId id1 = new MailboxFolderId(getRandomId());
|
||||||
|
MailboxFolderId id2 = new MailboxFolderId(getRandomId());
|
||||||
|
String validResponse1 = "{\"folders\": [ {\"id\": \"" + id1 + "\"} ] }";
|
||||||
|
String validResponse2 = "{\"folders\": [ {\"id\": \"" + id1 + "\"}, " +
|
||||||
|
"{ \"id\": \"" + id2 + "\"} ] }";
|
||||||
|
String invalidResponse1 = "{\"folders\":\"bar\"}";
|
||||||
|
String invalidResponse2 = "{\"folders\":{\"foo\":\"bar\"}}";
|
||||||
|
String invalidResponse3 =
|
||||||
|
"{\"folders\": [ {\"id\": \"" + id1 + "\", 1] }";
|
||||||
|
String invalidResponse4 = "{\"files\": [ 1, 2 ] }";
|
||||||
|
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
server.enqueue(new MockResponse().setBody(validResponse1));
|
||||||
|
server.enqueue(new MockResponse().setBody(validResponse2));
|
||||||
|
server.enqueue(new MockResponse());
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse1));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse2));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse3));
|
||||||
|
server.enqueue(new MockResponse().setBody(invalidResponse4));
|
||||||
|
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);
|
||||||
|
|
||||||
|
// valid response with one folders
|
||||||
|
assertEquals(singletonList(id1), api.getFolders(properties));
|
||||||
|
RecordedRequest request1 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request1.getPath());
|
||||||
|
assertEquals("GET", request1.getMethod());
|
||||||
|
assertToken(request1, token);
|
||||||
|
|
||||||
|
// valid response with two folders
|
||||||
|
assertEquals(Arrays.asList(id1, id2), api.getFolders(properties));
|
||||||
|
RecordedRequest request2 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request1.getPath());
|
||||||
|
assertEquals("GET", request2.getMethod());
|
||||||
|
assertToken(request2, token);
|
||||||
|
|
||||||
|
// empty body
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request3 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request3.getPath());
|
||||||
|
assertEquals("GET", request3.getMethod());
|
||||||
|
assertToken(request3, token);
|
||||||
|
|
||||||
|
// invalid response: string instead of list
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request4 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request4.getPath());
|
||||||
|
assertEquals("GET", request4.getMethod());
|
||||||
|
assertToken(request4, token);
|
||||||
|
|
||||||
|
// invalid response: object instead of list
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request5 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request5.getPath());
|
||||||
|
assertEquals("GET", request5.getMethod());
|
||||||
|
assertToken(request5, token);
|
||||||
|
|
||||||
|
// invalid response: list with non-objects
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request6 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request6.getPath());
|
||||||
|
assertEquals("GET", request6.getMethod());
|
||||||
|
assertToken(request6, token);
|
||||||
|
|
||||||
|
// no folders key in root object
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request7 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request7.getPath());
|
||||||
|
assertEquals("GET", request7.getMethod());
|
||||||
|
assertToken(request7, token);
|
||||||
|
|
||||||
|
// 401 not authorized
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request8 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request8.getPath());
|
||||||
|
assertEquals("GET", request8.getMethod());
|
||||||
|
assertToken(request8, token);
|
||||||
|
|
||||||
|
// 500 internal server error
|
||||||
|
assertThrows(ApiException.class, () -> api.getFolders(properties));
|
||||||
|
RecordedRequest request9 = server.takeRequest();
|
||||||
|
assertEquals("/folders", request9.getPath());
|
||||||
|
assertEquals("GET", request9.getMethod());
|
||||||
|
assertToken(request9, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFoldersOnlyForOwner() {
|
||||||
|
MailboxProperties properties =
|
||||||
|
new MailboxProperties("", token, false);
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
api.getFolders(properties));
|
||||||
|
}
|
||||||
|
|
||||||
private String getBaseUrl(MockWebServer server) {
|
private String getBaseUrl(MockWebServer server) {
|
||||||
String baseUrl = server.url("").toString();
|
String baseUrl = server.url("").toString();
|
||||||
return baseUrl.substring(0, baseUrl.length() - 1);
|
return baseUrl.substring(0, baseUrl.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertToken(RecordedRequest request, String token) {
|
private void assertToken(RecordedRequest request, MailboxId token) {
|
||||||
assertNotNull(request.getHeader("Authorization"));
|
assertNotNull(request.getHeader("Authorization"));
|
||||||
assertEquals("Bearer " + token, request.getHeader("Authorization"));
|
assertEquals("Bearer " + token, request.getHeader("Authorization"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,26 @@ package org.briarproject.bramble.mailbox;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
@@ -22,8 +31,12 @@ import okhttp3.OkHttpClient;
|
|||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxSecret;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.readBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.writeBytes;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -31,9 +44,20 @@ import static org.junit.Assume.assumeTrue;
|
|||||||
|
|
||||||
public class MailboxIntegrationTest extends BrambleTestCase {
|
public class MailboxIntegrationTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder folder = new TemporaryFolder();
|
||||||
|
|
||||||
private final static String URL_BASE = "http://127.0.0.1:8000";
|
private final static String URL_BASE = "http://127.0.0.1:8000";
|
||||||
private final static String SETUP_TOKEN =
|
private final static MailboxAuthToken SETUP_TOKEN;
|
||||||
"54686973206973206120736574757020746f6b656e20666f722042726961722e";
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
SETUP_TOKEN = MailboxAuthToken.fromString(
|
||||||
|
"54686973206973206120736574757020746f6b656e20666f722042726961722e");
|
||||||
|
} catch (InvalidMailboxIdException e) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final OkHttpClient client = new OkHttpClient.Builder()
|
private final OkHttpClient client = new OkHttpClient.Builder()
|
||||||
.socketFactory(SocketFactory.getDefault())
|
.socketFactory(SocketFactory.getDefault())
|
||||||
@@ -64,7 +88,7 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
|||||||
if (ownerProperties != null) return;
|
if (ownerProperties != null) return;
|
||||||
MailboxProperties setupProperties =
|
MailboxProperties setupProperties =
|
||||||
new MailboxProperties(URL_BASE, SETUP_TOKEN, true);
|
new MailboxProperties(URL_BASE, SETUP_TOKEN, true);
|
||||||
String ownerToken = api.setup(setupProperties);
|
MailboxAuthToken ownerToken = api.setup(setupProperties);
|
||||||
ownerProperties = new MailboxProperties(URL_BASE, ownerToken, true);
|
ownerProperties = new MailboxProperties(URL_BASE, ownerToken, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +125,128 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
|||||||
() -> api.deleteContact(ownerProperties, contactId2));
|
() -> api.deleteContact(ownerProperties, contactId2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileManagementApi() throws Exception {
|
||||||
|
// add contact, so we can leave each other files
|
||||||
|
ContactId contactId = new ContactId(1);
|
||||||
|
MailboxContact contact = getMailboxContact(contactId);
|
||||||
|
MailboxProperties contactProperties = new MailboxProperties(
|
||||||
|
ownerProperties.getOnionAddress(), contact.token, false);
|
||||||
|
api.addContact(ownerProperties, contact);
|
||||||
|
|
||||||
|
// upload a file for our contact
|
||||||
|
File file1 = folder.newFile();
|
||||||
|
byte[] bytes1 = getRandomBytes(2048);
|
||||||
|
writeBytes(file1, bytes1);
|
||||||
|
api.addFile(ownerProperties, contact.inboxId, file1);
|
||||||
|
|
||||||
|
// contact checks files
|
||||||
|
List<MailboxFile> files1 =
|
||||||
|
api.getFiles(contactProperties, contact.inboxId);
|
||||||
|
assertEquals(1, files1.size());
|
||||||
|
MailboxFileId fileName1 = files1.get(0).name;
|
||||||
|
|
||||||
|
// owner can't check files
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(ownerProperties, contact.inboxId));
|
||||||
|
|
||||||
|
// contact downloads file
|
||||||
|
File file1downloaded = folder.newFile();
|
||||||
|
api.getFile(contactProperties, contact.inboxId, fileName1,
|
||||||
|
file1downloaded);
|
||||||
|
assertArrayEquals(bytes1, readBytes(file1downloaded));
|
||||||
|
|
||||||
|
// owner can't download file, even if knowing name
|
||||||
|
File file1forbidden = folder.newFile();
|
||||||
|
assertThrows(ApiException.class, () -> api.getFile(ownerProperties,
|
||||||
|
contact.inboxId, fileName1, file1forbidden));
|
||||||
|
assertEquals(0, file1forbidden.length());
|
||||||
|
|
||||||
|
// owner can't delete file
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.deleteFile(ownerProperties, contact.inboxId, fileName1));
|
||||||
|
|
||||||
|
// contact deletes file
|
||||||
|
api.deleteFile(contactProperties, contact.inboxId, fileName1);
|
||||||
|
assertEquals(0,
|
||||||
|
api.getFiles(contactProperties, contact.inboxId).size());
|
||||||
|
|
||||||
|
// contact uploads two files for the owner
|
||||||
|
File file2 = folder.newFile();
|
||||||
|
File file3 = folder.newFile();
|
||||||
|
byte[] bytes2 = getRandomBytes(2048);
|
||||||
|
byte[] bytes3 = getRandomBytes(1024);
|
||||||
|
writeBytes(file2, bytes2);
|
||||||
|
writeBytes(file3, bytes3);
|
||||||
|
api.addFile(contactProperties, contact.outboxId, file2);
|
||||||
|
api.addFile(contactProperties, contact.outboxId, file3);
|
||||||
|
|
||||||
|
// owner checks folders with available files
|
||||||
|
List<MailboxFolderId> folders = api.getFolders(ownerProperties);
|
||||||
|
assertEquals(singletonList(contact.outboxId), folders);
|
||||||
|
|
||||||
|
// owner lists files in contact's outbox
|
||||||
|
List<MailboxFile> files2 =
|
||||||
|
api.getFiles(ownerProperties, contact.outboxId);
|
||||||
|
assertEquals(2, files2.size());
|
||||||
|
MailboxFileId file2name = files2.get(0).name;
|
||||||
|
MailboxFileId file3name = files2.get(1).name;
|
||||||
|
|
||||||
|
// contact can't list files in contact's outbox
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.getFiles(contactProperties, contact.outboxId));
|
||||||
|
|
||||||
|
// owner downloads both files from contact's outbox
|
||||||
|
File file2downloaded = folder.newFile();
|
||||||
|
File file3downloaded = folder.newFile();
|
||||||
|
api.getFile(ownerProperties, contact.outboxId, file2name,
|
||||||
|
file2downloaded);
|
||||||
|
api.getFile(ownerProperties, contact.outboxId, file3name,
|
||||||
|
file3downloaded);
|
||||||
|
byte[] downloadedBytes2 = readBytes(file2downloaded);
|
||||||
|
byte[] downloadedBytes3 = readBytes(file3downloaded);
|
||||||
|
// file order is preserved (sorted by time),
|
||||||
|
// so we know what file is which
|
||||||
|
assertArrayEquals(bytes2, downloadedBytes2);
|
||||||
|
assertArrayEquals(bytes3, downloadedBytes3);
|
||||||
|
|
||||||
|
// contact can't download files again, even if knowing name
|
||||||
|
File file2forbidden = folder.newFile();
|
||||||
|
File file3forbidden = folder.newFile();
|
||||||
|
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
|
||||||
|
contact.outboxId, file2name, file2forbidden));
|
||||||
|
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
|
||||||
|
contact.outboxId, file3name, file3forbidden));
|
||||||
|
assertEquals(0, file1forbidden.length());
|
||||||
|
assertEquals(0, file2forbidden.length());
|
||||||
|
|
||||||
|
// contact can't delete files in outbox
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.deleteFile(contactProperties, contact.outboxId, file2name));
|
||||||
|
assertThrows(ApiException.class, () ->
|
||||||
|
api.deleteFile(contactProperties, contact.outboxId, file3name));
|
||||||
|
|
||||||
|
// owner deletes files
|
||||||
|
api.deleteFile(ownerProperties, contact.outboxId, file2name);
|
||||||
|
api.deleteFile(ownerProperties, contact.outboxId, file3name);
|
||||||
|
assertEquals(emptyList(),
|
||||||
|
api.getFiles(ownerProperties, contact.outboxId));
|
||||||
|
assertEquals(emptyList(), api.getFolders(ownerProperties));
|
||||||
|
|
||||||
|
// deleting a non-existent file is tolerable
|
||||||
|
assertThrows(TolerableFailureException.class, () ->
|
||||||
|
api.deleteFile(ownerProperties, contact.outboxId, file3name));
|
||||||
|
|
||||||
|
// owner deletes contact again to leave clean state for other tests
|
||||||
|
api.deleteContact(ownerProperties, contactId);
|
||||||
|
assertEquals(emptyList(), api.getContacts(ownerProperties));
|
||||||
|
}
|
||||||
|
|
||||||
private MailboxContact getMailboxContact(ContactId contactId) {
|
private MailboxContact getMailboxContact(ContactId contactId) {
|
||||||
return new MailboxContact(contactId, getMailboxSecret(),
|
MailboxAuthToken authToken = new MailboxAuthToken(getRandomId());
|
||||||
getMailboxSecret(), getMailboxSecret());
|
MailboxFolderId inboxId = new MailboxFolderId(getRandomId());
|
||||||
|
MailboxFolderId outboxId = new MailboxFolderId(getRandomId());
|
||||||
|
return new MailboxContact(contactId, authToken, inboxId, outboxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.mailbox;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||||
@@ -20,6 +21,7 @@ import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTIN
|
|||||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_TOKEN;
|
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_TOKEN;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_NAMESPACE;
|
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_UPLOADS_NAMESPACE;
|
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_UPLOADS_NAMESPACE;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -35,7 +37,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
new MailboxSettingsManagerImpl(settingsManager);
|
new MailboxSettingsManagerImpl(settingsManager);
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
private final String onion = getRandomString(64);
|
private final String onion = getRandomString(64);
|
||||||
private final String token = getRandomString(64);
|
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
|
||||||
private final ContactId contactId1 = new ContactId(random.nextInt());
|
private final ContactId contactId1 = new ContactId(random.nextInt());
|
||||||
private final ContactId contactId2 = new ContactId(random.nextInt());
|
private final ContactId contactId2 = new ContactId(random.nextInt());
|
||||||
private final ContactId contactId3 = new ContactId(random.nextInt());
|
private final ContactId contactId3 = new ContactId(random.nextInt());
|
||||||
@@ -62,7 +64,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
settings.put(SETTINGS_KEY_ONION, onion);
|
settings.put(SETTINGS_KEY_ONION, onion);
|
||||||
settings.put(SETTINGS_KEY_TOKEN, token);
|
settings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||||
@@ -81,7 +83,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Settings expectedSettings = new Settings();
|
Settings expectedSettings = new Settings();
|
||||||
expectedSettings.put(SETTINGS_KEY_ONION, onion);
|
expectedSettings.put(SETTINGS_KEY_ONION, onion);
|
||||||
expectedSettings.put(SETTINGS_KEY_TOKEN, token);
|
expectedSettings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||||
MailboxProperties properties =
|
MailboxProperties properties =
|
||||||
new MailboxProperties(onion, token, true);
|
new MailboxProperties(onion, token, true);
|
||||||
|
|
||||||
@@ -180,7 +182,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
settings.put(String.valueOf(contactId1.getInt()), onion);
|
settings.put(String.valueOf(contactId1.getInt()), onion);
|
||||||
settings.put(String.valueOf(contactId2.getInt()), token);
|
settings.put(String.valueOf(contactId2.getInt()), token.toString());
|
||||||
settings.put(String.valueOf(contactId3.getInt()), "");
|
settings.put(String.valueOf(contactId3.getInt()), "");
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -192,7 +194,8 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
String filename1 = manager.getPendingUpload(txn, contactId1);
|
String filename1 = manager.getPendingUpload(txn, contactId1);
|
||||||
assertEquals(onion, filename1);
|
assertEquals(onion, filename1);
|
||||||
String filename2 = manager.getPendingUpload(txn, contactId2);
|
String filename2 = manager.getPendingUpload(txn, contactId2);
|
||||||
assertEquals(token, filename2);
|
assertNotNull(filename2);
|
||||||
|
assertEquals(token.toString(), filename2);
|
||||||
String filename3 = manager.getPendingUpload(txn, contactId3);
|
String filename3 = manager.getPendingUpload(txn, contactId3);
|
||||||
assertNull(filename3);
|
assertNull(filename3);
|
||||||
String filename4 =
|
String filename4 =
|
||||||
|
|||||||
Reference in New Issue
Block a user