mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-27 00:53:18 +01:00
Make stored messages REST accessible.
Add REST endpoints for retrieving and acknowledging pending messges, beyond the WebSocket. // FREEBIE
This commit is contained in:
@@ -20,9 +20,11 @@ import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@@ -45,8 +47,10 @@ public class FederatedControllerTest {
|
||||
private static final String MULTI_DEVICE_RECIPIENT = "+14152222222";
|
||||
|
||||
private PushSender pushSender = mock(PushSender.class );
|
||||
private ReceiptSender receiptSender = mock(ReceiptSender.class);
|
||||
private FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
|
||||
private AccountsManager accountsManager = mock(AccountsManager.class );
|
||||
private MessagesManager messagesManager = mock(MessagesManager.class);
|
||||
private RateLimiters rateLimiters = mock(RateLimiters.class );
|
||||
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||
|
||||
@@ -55,7 +59,7 @@ public class FederatedControllerTest {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private final MessageController messageController = new MessageController(rateLimiters, pushSender, accountsManager, federatedClientManager);
|
||||
private final MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager);
|
||||
private final KeysControllerV2 keysControllerV2 = mock(KeysControllerV2.class);
|
||||
|
||||
@Rule
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.base.Optional;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -10,19 +11,26 @@ import org.whispersystems.textsecuregcm.controllers.MessageController;
|
||||
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
import org.whispersystems.textsecuregcm.entities.MismatchedDevices;
|
||||
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
|
||||
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.StaleDevices;
|
||||
import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -30,6 +38,7 @@ import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
@@ -42,8 +51,10 @@ public class MessageControllerTest {
|
||||
private static final String MULTI_DEVICE_RECIPIENT = "+14152222222";
|
||||
|
||||
private final PushSender pushSender = mock(PushSender.class );
|
||||
private final ReceiptSender receiptSender = mock(ReceiptSender.class);
|
||||
private final FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
|
||||
private final AccountsManager accountsManager = mock(AccountsManager.class );
|
||||
private final MessagesManager messagesManager = mock(MessagesManager.class);
|
||||
private final RateLimiters rateLimiters = mock(RateLimiters.class );
|
||||
private final RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||
|
||||
@@ -52,8 +63,8 @@ public class MessageControllerTest {
|
||||
@Rule
|
||||
public final ResourceTestRule resources = ResourceTestRule.builder()
|
||||
.addProvider(AuthHelper.getAuthenticator())
|
||||
.addResource(new MessageController(rateLimiters, pushSender, accountsManager,
|
||||
federatedClientManager))
|
||||
.addResource(new MessageController(rateLimiters, pushSender, receiptSender, accountsManager,
|
||||
messagesManager, federatedClientManager))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -175,4 +186,75 @@ public class MessageControllerTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public synchronized void testGetMessages() throws Exception {
|
||||
|
||||
final long timestampOne = 313377;
|
||||
final long timestampTwo = 313388;
|
||||
|
||||
List<OutgoingMessageEntity> messages = new LinkedList<OutgoingMessageEntity>() {{
|
||||
add(new OutgoingMessageEntity(1L, MessageProtos.OutgoingMessageSignal.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes()));
|
||||
add(new OutgoingMessageEntity(2L, MessageProtos.OutgoingMessageSignal.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null));
|
||||
}};
|
||||
|
||||
when(messagesManager.getMessagesForDevice(eq(AuthHelper.VALID_NUMBER), eq(1L))).thenReturn(messages);
|
||||
|
||||
OutgoingMessageEntityList response =
|
||||
resources.client().resource("/v1/messages/")
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(OutgoingMessageEntityList.class);
|
||||
|
||||
|
||||
assertEquals(response.getMessages().size(), 2);
|
||||
|
||||
assertEquals(response.getMessages().get(0).getId(), 0);
|
||||
assertEquals(response.getMessages().get(1).getId(), 0);
|
||||
|
||||
assertEquals(response.getMessages().get(0).getTimestamp(), timestampOne);
|
||||
assertEquals(response.getMessages().get(1).getTimestamp(), timestampTwo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public synchronized void testDeleteMessages() throws Exception {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 31337))
|
||||
.thenReturn(Optional.of(new OutgoingMessageEntity(31337L,
|
||||
MessageProtos.OutgoingMessageSignal.Type.CIPHERTEXT_VALUE,
|
||||
null, timestamp,
|
||||
"+14152222222", 1, "hi".getBytes())));
|
||||
|
||||
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 31338))
|
||||
.thenReturn(Optional.of(new OutgoingMessageEntity(31337L,
|
||||
MessageProtos.OutgoingMessageSignal.Type.RECEIPT_VALUE,
|
||||
null, System.currentTimeMillis(),
|
||||
"+14152222222", 1, null)));
|
||||
|
||||
|
||||
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 31339))
|
||||
.thenReturn(Optional.<OutgoingMessageEntity>absent());
|
||||
|
||||
ClientResponse response = resources.client().resource(String.format("/v1/messages/%d", 31337))
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.delete(ClientResponse.class);
|
||||
|
||||
assertThat("Good Response Code", response.getStatus(), is(equalTo(204)));
|
||||
verify(receiptSender).sendReceipt(any(Account.class), eq("+14152222222"), eq(timestamp), eq(Optional.<String>absent()));
|
||||
|
||||
response = resources.client().resource(String.format("/v1/messages/%d", 31338))
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.delete(ClientResponse.class);
|
||||
|
||||
assertThat("Good Response Code", response.getStatus(), is(equalTo(204)));
|
||||
verifyNoMoreInteractions(receiptSender);
|
||||
|
||||
response = resources.client().resource(String.format("/v1/messages/%d", 31339))
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.delete(ClientResponse.class);
|
||||
|
||||
assertThat("Good Response Code", response.getStatus(), is(equalTo(204)));
|
||||
verifyNoMoreInteractions(receiptSender);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.whispersystems.textsecuregcm.controllers.ReceiptController;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
@@ -32,12 +33,14 @@ public class ReceiptControllerTest {
|
||||
private final FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
|
||||
private final AccountsManager accountsManager = mock(AccountsManager.class );
|
||||
|
||||
private final ReceiptSender receiptSender = new ReceiptSender(accountsManager, pushSender, federatedClientManager);
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Rule
|
||||
public final ResourceTestRule resources = ResourceTestRule.builder()
|
||||
.addProvider(AuthHelper.getAuthenticator())
|
||||
.addResource(new ReceiptController(accountsManager, federatedClientManager, pushSender))
|
||||
.addResource(new ReceiptController(receiptSender))
|
||||
.build();
|
||||
|
||||
@Before
|
||||
|
||||
@@ -9,7 +9,9 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
@@ -57,12 +59,13 @@ public class WebSocketConnectionTest {
|
||||
private static final Device device = mock(Device.class );
|
||||
private static final UpgradeRequest upgradeRequest = mock(UpgradeRequest.class );
|
||||
private static final PushSender pushSender = mock(PushSender.class);
|
||||
private static final ReceiptSender receiptSender = mock(ReceiptSender.class);
|
||||
|
||||
@Test
|
||||
public void testCredentials() throws Exception {
|
||||
MessagesManager storedMessages = mock(MessagesManager.class);
|
||||
WebSocketAccountAuthenticator webSocketAuthenticator = new WebSocketAccountAuthenticator(accountAuthenticator);
|
||||
AuthenticatedConnectListener connectListener = new AuthenticatedConnectListener(accountsManager, pushSender, storedMessages, pubSubManager);
|
||||
AuthenticatedConnectListener connectListener = new AuthenticatedConnectListener(accountsManager, pushSender, receiptSender, storedMessages, pubSubManager);
|
||||
WebSocketSessionContext sessionContext = mock(WebSocketSessionContext.class);
|
||||
|
||||
when(accountAuthenticator.authenticate(eq(new BasicCredentials(VALID_USER, VALID_PASSWORD))))
|
||||
@@ -98,10 +101,10 @@ public class WebSocketConnectionTest {
|
||||
public void testOpen() throws Exception {
|
||||
MessagesManager storedMessages = mock(MessagesManager.class);
|
||||
|
||||
List<Pair<Long, OutgoingMessageSignal>> outgoingMessages = new LinkedList<Pair<Long, OutgoingMessageSignal>> () {{
|
||||
add(new Pair<>(1L, createMessage("sender1", 1111, false, "first")));
|
||||
add(new Pair<>(2L, createMessage("sender1", 2222, false, "second")));
|
||||
add(new Pair<>(3L, createMessage("sender2", 3333, false, "third")));
|
||||
List<OutgoingMessageEntity> outgoingMessages = new LinkedList<OutgoingMessageEntity> () {{
|
||||
add(createMessage(1L, "sender1", 1111, false, "first"));
|
||||
add(createMessage(2L, "sender1", 2222, false, "second"));
|
||||
add(createMessage(3L, "sender2", 3333, false, "third"));
|
||||
}};
|
||||
|
||||
when(device.getId()).thenReturn(2L);
|
||||
@@ -139,7 +142,7 @@ public class WebSocketConnectionTest {
|
||||
});
|
||||
|
||||
WebsocketAddress websocketAddress = new WebsocketAddress(account.getNumber(), device.getId());
|
||||
WebSocketConnection connection = new WebSocketConnection(accountsManager, pushSender, storedMessages,
|
||||
WebSocketConnection connection = new WebSocketConnection(pushSender, receiptSender, storedMessages,
|
||||
account, device, client);
|
||||
|
||||
connection.onDispatchSubscribed(websocketAddress.serialize());
|
||||
@@ -154,25 +157,29 @@ public class WebSocketConnectionTest {
|
||||
futures.get(0).setException(new IOException());
|
||||
futures.get(2).setException(new IOException());
|
||||
|
||||
List<OutgoingMessageSignal> pending = new LinkedList<OutgoingMessageSignal>() {{
|
||||
add(createMessage("sender1", 1111, false, "first"));
|
||||
add(createMessage("sender2", 3333, false, "third"));
|
||||
}};
|
||||
// List<OutgoingMessageSignal> pending = new LinkedList<OutgoingMessageSignal>() {{
|
||||
// add(createMessage("sender1", 1111, false, "first"));
|
||||
// add(createMessage("sender2", 3333, false, "third"));
|
||||
// }};
|
||||
|
||||
verify(pushSender, times(1)).sendMessage(eq(sender1), eq(sender1device), any(OutgoingMessageSignal.class));
|
||||
verify(storedMessages, times(1)).delete(eq(2L));
|
||||
|
||||
// verify(pushSender, times(1)).sendMessage(eq(sender1), eq(sender1device), any(OutgoingMessageSignal.class));
|
||||
|
||||
connection.onDispatchUnsubscribed(websocketAddress.serialize());
|
||||
verify(client).close(anyInt(), anyString());
|
||||
}
|
||||
|
||||
private OutgoingMessageSignal createMessage(String sender, long timestamp, boolean receipt, String content) {
|
||||
return OutgoingMessageSignal.newBuilder()
|
||||
.setSource(sender)
|
||||
.setSourceDevice(1)
|
||||
.setType(receipt ? OutgoingMessageSignal.Type.RECEIPT_VALUE : OutgoingMessageSignal.Type.CIPHERTEXT_VALUE)
|
||||
.setTimestamp(timestamp)
|
||||
.setMessage(ByteString.copyFrom(content.getBytes()))
|
||||
.build();
|
||||
private OutgoingMessageEntity createMessage(long id, String sender, long timestamp, boolean receipt, String content) {
|
||||
return new OutgoingMessageEntity(id, receipt ? OutgoingMessageSignal.Type.RECEIPT_VALUE : OutgoingMessageSignal.Type.CIPHERTEXT_VALUE,
|
||||
null, timestamp, sender, 1, content.getBytes());
|
||||
// return OutgoingMessageSignal.newBuilder()
|
||||
// .setSource(sender)
|
||||
// .setSourceDevice(1)
|
||||
// .setType(receipt ? OutgoingMessageSignal.Type.RECEIPT_VALUE : OutgoingMessageSignal.Type.CIPHERTEXT_VALUE)
|
||||
// .setTimestamp(timestamp)
|
||||
// .setMessage(ByteString.copyFrom(content.getBytes()))
|
||||
// .build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user