Skip to content

Commit

Permalink
Fix for federated message flow to support source IDs.
Browse files Browse the repository at this point in the history
  • Loading branch information
moxie0 committed Feb 24, 2014
1 parent 3e6d862 commit 5085d40
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 18 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<groupId>org.whispersystems.textsecure</groupId>
<artifactId>TextSecureServer</artifactId>
<version>0.4</version>
<version>0.5</version>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public AttachmentUri getSignedAttachmentUri(@Auth Federated
@PathParam("attachmentId") long attachmentId)
throws IOException
{
return attachmentController.redirectToAttachment(new NonLimitedAccount("Unknown", peer.getName()),
return attachmentController.redirectToAttachment(new NonLimitedAccount("Unknown", -1, peer.getName()),
attachmentId, Optional.<String>absent());
}

Expand All @@ -89,7 +89,7 @@ public PreKey getKey(@Auth FederatedPeer peer,
throws IOException
{
try {
return keysController.get(new NonLimitedAccount("Unknown", peer.getName()), number, Optional.<String>absent());
return keysController.get(new NonLimitedAccount("Unknown", -1, peer.getName()), number, Optional.<String>absent());
} catch (RateLimitExceededException e) {
logger.warn("Rate limiting on federated channel", e);
throw new IOException(e);
Expand All @@ -106,7 +106,7 @@ public UnstructuredPreKeyList getKeys(@Auth FederatedPeer peer,
throws IOException
{
try {
return keysController.getDeviceKey(new NonLimitedAccount("Unknown", peer.getName()),
return keysController.getDeviceKey(new NonLimitedAccount("Unknown", -1, peer.getName()),
number, device, Optional.<String>absent());
} catch (RateLimitExceededException e) {
logger.warn("Rate limiting on federated channel", e);
Expand All @@ -116,16 +116,17 @@ public UnstructuredPreKeyList getKeys(@Auth FederatedPeer peer,

@Timed
@PUT
@Path("/messages/{source}/{destination}")
public void sendMessages(@Auth FederatedPeer peer,
@PathParam("source") String source,
@PathParam("destination") String destination,
@Valid IncomingMessageList messages)
@Path("/messages/{source}/{sourceDeviceId}/{destination}")
public void sendMessages(@Auth FederatedPeer peer,
@PathParam("source") String source,
@PathParam("sourceDeviceId") long sourceDeviceId,
@PathParam("destination") String destination,
@Valid IncomingMessageList messages)
throws IOException
{
try {
messages.setRelay(null);
messageController.sendMessage(new NonLimitedAccount(source, peer.getName()), destination, messages);
messageController.sendMessage(new NonLimitedAccount(source, sourceDeviceId, peer.getName()), destination, messages);
} catch (RateLimitExceededException e) {
logger.warn("Rate limiting on federated channel", e);
throw new IOException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ private void sendRelayMessage(Account source,
{
try {
FederatedClient client = federatedClientManager.getClient(messages.getRelay());
client.sendMessages(source.getNumber(), destinationName, messages);
client.sendMessages(source.getNumber(), source.getAuthenticatedDevice().get().getId(),
destinationName, messages);
} catch (NoSuchPeerException e) {
throw new NoSuchUserException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class FederatedClient {

private static final String USER_COUNT_PATH = "/v1/federation/user_count";
private static final String USER_TOKENS_PATH = "/v1/federation/user_tokens/%d";
private static final String RELAY_MESSAGE_PATH = "/v1/federation/messages/%s/%s";
private static final String RELAY_MESSAGE_PATH = "/v1/federation/messages/%s/%d/%s";
private static final String PREKEY_PATH_DEVICE = "/v1/federation/key/%s/%s";
private static final String ATTACHMENT_URI_PATH = "/v1/federation/attachment/%d";

Expand Down Expand Up @@ -155,11 +155,11 @@ public List<ClientContact> getUserTokens(int offset) {
}
}

public void sendMessages(String source, String destination, IncomingMessageList messages)
public void sendMessages(String source, long sourceDeviceId, String destination, IncomingMessageList messages)
throws IOException
{
try {
WebResource resource = client.resource(peer.getUrl()).path(String.format(RELAY_MESSAGE_PATH, source, destination));
WebResource resource = client.resource(peer.getUrl()).path(String.format(RELAY_MESSAGE_PATH, source, sourceDeviceId, destination));
ClientResponse response = resource.type(MediaType.APPLICATION_JSON)
.header("Authorization", authorizationHeader)
.entity(messages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Optional;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;

public class NonLimitedAccount extends Account {

Expand All @@ -13,20 +14,32 @@ public class NonLimitedAccount extends Account {
@JsonIgnore
private final String relay;

public NonLimitedAccount(String number, String relay) {
this.number = number;
this.relay = relay;
@JsonIgnore
private final long deviceId;

public NonLimitedAccount(String number, long deviceId, String relay) {
this.number = number;
this.deviceId = deviceId;
this.relay = relay;
}

@Override
public String getNumber() {
return number;
}

@Override
public boolean isRateLimited() {
return false;
}

@Override
public Optional<String> getRelay() {
return Optional.of(relay);
}

@Override
public Optional<Device> getAuthenticatedDevice() {
return Optional.of(new Device(deviceId, null, null, null, null, null, false, 0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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 com.yammer.dropwizard.testing.ResourceTest;
import org.junit.Test;
import org.whispersystems.textsecuregcm.controllers.FederationController;
import org.whispersystems.textsecuregcm.controllers.MessageController;
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
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.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;

import javax.ws.rs.core.MediaType;
import java.util.LinkedList;
import java.util.List;

import static com.yammer.dropwizard.testing.JsonHelpers.jsonFixture;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class FederatedControllerTest extends ResourceTest {

private static final String SINGLE_DEVICE_RECIPIENT = "+14151111111";
private static final String MULTI_DEVICE_RECIPIENT = "+14152222222";

private PushSender pushSender = mock(PushSender.class );
private FederatedClientManager federatedClientManager = mock(FederatedClientManager.class);
private AccountsManager accountsManager = mock(AccountsManager.class );
private RateLimiters rateLimiters = mock(RateLimiters.class );
private RateLimiter rateLimiter = mock(RateLimiter.class );

private final ObjectMapper mapper = new ObjectMapper();

@Override
protected void setUpResources() throws Exception {
addProvider(AuthHelper.getAuthenticator());

List<Device> singleDeviceList = new LinkedList<Device>() {{
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111));
}};

List<Device> multiDeviceList = new LinkedList<Device>() {{
add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222));
add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333));
}};

Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList);
Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, false, multiDeviceList);

when(accountsManager.get(eq(SINGLE_DEVICE_RECIPIENT))).thenReturn(Optional.of(singleDeviceAccount));
when(accountsManager.get(eq(MULTI_DEVICE_RECIPIENT))).thenReturn(Optional.of(multiDeviceAccount));

when(rateLimiters.getMessagesLimiter()).thenReturn(rateLimiter);

MessageController messageController = new MessageController(rateLimiters, pushSender, accountsManager, federatedClientManager);
addResource(new FederationController(accountsManager, null, null, messageController));
}

@Test
public void testSingleDeviceCurrent() throws Exception {
ClientResponse response =
client().resource(String.format("/v1/federation/messages/+14152223333/1/%s", SINGLE_DEVICE_RECIPIENT))
.header("Authorization", AuthHelper.getAuthHeader("cyanogen", "foofoo"))
.entity(mapper.readValue(jsonFixture("fixtures/current_message_single_device.json"), IncomingMessageList.class))
.type(MediaType.APPLICATION_JSON_TYPE)
.put(ClientResponse.class);

assertThat("Good Response", response.getStatus(), is(equalTo(204)));

verify(pushSender).sendMessage(any(Account.class), any(Device.class), any(MessageProtos.OutgoingMessageSignal.class));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.whispersystems.textsecuregcm.util.Base64;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -40,7 +42,14 @@ public static MultiBasicAuthProvider<FederatedPeer, Account> getAuthenticator()
when(account.getRelay()).thenReturn(Optional.<String>absent());
when(accounts.get(VALID_NUMBER)).thenReturn(Optional.of(account));

return new MultiBasicAuthProvider<>(new FederatedPeerAuthenticator(new FederationConfiguration()),
List<FederatedPeer> peer = new LinkedList<FederatedPeer>() {{
add(new FederatedPeer("cyanogen", "https://foo", "foofoo", "bazzzzz"));
}};

FederationConfiguration federationConfiguration = mock(FederationConfiguration.class);
when(federationConfiguration.getPeers()).thenReturn(peer);

return new MultiBasicAuthProvider<>(new FederatedPeerAuthenticator(federationConfiguration),
FederatedPeer.class,
new AccountAuthenticator(accounts),
Account.class, "WhisperServer");
Expand Down

0 comments on commit 5085d40

Please sign in to comment.