From 45a0b74b89c5df022c6a9052f54f8079fb01a93d Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 21 Jan 2015 15:15:40 -0800 Subject: [PATCH] Device provisioning fixes. // FREEBIE --- .../controllers/DeviceController.java | 2 + .../controllers/ReceiptController.java | 6 +-- .../entities/PreKeyResponseV2.java | 17 ++++++++- .../federation/NonLimitedAccount.java | 2 +- .../textsecuregcm/storage/Account.java | 19 +++++----- .../textsecuregcm/storage/Device.java | 34 ++++++++++++++++- .../textsecuregcm/util/Util.java | 5 +++ .../AuthenticatedConnectListener.java | 6 +++ .../controllers/FederatedControllerTest.java | 12 +++--- .../tests/controllers/KeyControllerTest.java | 38 ++++++++++--------- .../controllers/MessageControllerTest.java | 19 ++++++---- .../controllers/ReceiptControllerTest.java | 13 ++++--- .../websocket/WebSocketConnectionTest.java | 4 +- 13 files changed, 123 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java index 4ceacfe86..b41524f7f 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java +++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceController.java @@ -31,6 +31,7 @@ import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.PendingDevicesManager; +import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.VerificationCode; import javax.validation.Valid; @@ -119,6 +120,7 @@ public class DeviceController { device.setSignalingKey(accountAttributes.getSignalingKey()); device.setFetchesMessages(accountAttributes.getFetchesMessages()); device.setId(account.get().getNextDeviceId()); + device.setLastSeen(Util.todayInMillis()); account.get().addDevice(device); accounts.update(account.get()); diff --git a/src/main/java/org/whispersystems/textsecuregcm/controllers/ReceiptController.java b/src/main/java/org/whispersystems/textsecuregcm/controllers/ReceiptController.java index 4a5ef2450..13bd12242 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/controllers/ReceiptController.java +++ b/src/main/java/org/whispersystems/textsecuregcm/controllers/ReceiptController.java @@ -18,7 +18,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import java.io.IOException; -import java.util.List; +import java.util.Set; import io.dropwizard.auth.Auth; import static org.whispersystems.textsecuregcm.entities.MessageProtos.OutgoingMessageSignal; @@ -74,8 +74,8 @@ public class ReceiptController { private void sendDirectReceipt(Account source, String destination, long messageId) throws NotPushRegisteredException, TransientPushFailureException, NoSuchUserException { - Account destinationAccount = getDestinationAccount(destination); - List destinationDevices = destinationAccount.getDevices(); + Account destinationAccount = getDestinationAccount(destination); + Set destinationDevices = destinationAccount.getDevices(); OutgoingMessageSignal.Builder message = OutgoingMessageSignal.newBuilder() diff --git a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java index ac11fe345..4f3e779fe 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java +++ b/src/main/java/org/whispersystems/textsecuregcm/entities/PreKeyResponseV2.java @@ -16,6 +16,7 @@ */ package org.whispersystems.textsecuregcm.entities; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -42,7 +43,19 @@ public class PreKeyResponseV2 { } @VisibleForTesting - public List getDevices() { - return devices; + @JsonIgnore + public PreKeyResponseItemV2 getDevice(int deviceId) { + for (PreKeyResponseItemV2 device : devices) { + if (device.getDeviceId() == deviceId) return device; + } + + return null; } + + @VisibleForTesting + @JsonIgnore + public int getDevicesCount() { + return devices.size(); + } + } diff --git a/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java b/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java index 2c9b5f817..2bcfcd1a9 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java +++ b/src/main/java/org/whispersystems/textsecuregcm/federation/NonLimitedAccount.java @@ -40,6 +40,6 @@ public class NonLimitedAccount extends Account { @Override public Optional getAuthenticatedDevice() { - return Optional.of(new Device(deviceId, null, null, null, null, null, false, 0, null)); + return Optional.of(new Device(deviceId, null, null, null, null, null, false, 0, null, System.currentTimeMillis())); } } diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java index 78c240de1..62d9e9f8b 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java +++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java @@ -22,8 +22,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; -import java.util.LinkedList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class Account { @@ -36,7 +36,7 @@ public class Account { private boolean supportsSms; @JsonProperty - private List devices = new LinkedList<>(); + private Set devices = new HashSet<>(); @JsonProperty private String identityKey; @@ -47,7 +47,7 @@ public class Account { public Account() {} @VisibleForTesting - public Account(String number, boolean supportsSms, List devices) { + public Account(String number, boolean supportsSms, Set devices) { this.number = number; this.supportsSms = supportsSms; this.devices = devices; @@ -78,14 +78,11 @@ public class Account { } public void addDevice(Device device) { + this.devices.remove(device); this.devices.add(device); } - public void setDevices(List devices) { - this.devices = devices; - } - - public List getDevices() { + public Set getDevices() { return devices; } @@ -113,7 +110,9 @@ public class Account { long highestDevice = Device.MASTER_ID; for (Device device : devices) { - if (device.getId() > highestDevice) { + if (!device.isActive()) { + return device.getId(); + } else if (device.getId() > highestDevice) { highestDevice = device.getId(); } } diff --git a/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java b/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java index c7d81ad8f..0554cc8d4 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java +++ b/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java @@ -22,6 +22,8 @@ import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials; import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.util.Util; +import java.util.concurrent.TimeUnit; + public class Device { public static final long MASTER_ID = 1; @@ -56,12 +58,15 @@ public class Device { @JsonProperty private SignedPreKey signedPreKey; + @JsonProperty + private long lastSeen; + public Device() {} public Device(long id, String authToken, String salt, String signalingKey, String gcmId, String apnId, boolean fetchesMessages, int registrationId, - SignedPreKey signedPreKey) + SignedPreKey signedPreKey, long lastSeen) { this.id = id; this.authToken = authToken; @@ -72,6 +77,7 @@ public class Device { this.fetchesMessages = fetchesMessages; this.registrationId = registrationId; this.signedPreKey = signedPreKey; + this.lastSeen = lastSeen; } public String getApnId() { @@ -86,6 +92,14 @@ public class Device { } } + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + } + + public long getLastSeen() { + return lastSeen; + } + public String getGcmId() { return gcmId; } @@ -124,7 +138,10 @@ public class Device { } public boolean isActive() { - return fetchesMessages || !Util.isEmpty(getApnId()) || !Util.isEmpty(getGcmId()); + boolean hasChannel = fetchesMessages || !Util.isEmpty(getApnId()) || !Util.isEmpty(getGcmId()); + + return (id == MASTER_ID && hasChannel) || + (id != MASTER_ID && hasChannel && signedPreKey != null && lastSeen > (System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30))); } public boolean getFetchesMessages() { @@ -158,4 +175,17 @@ public class Device { public long getPushTimestamp() { return pushTimestamp; } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof Device)) return false; + + Device that = (Device)other; + return this.id == that.id; + } + + @Override + public int hashCode() { + return (int)this.id; + } } diff --git a/src/main/java/org/whispersystems/textsecuregcm/util/Util.java b/src/main/java/org/whispersystems/textsecuregcm/util/Util.java index b1e5c256b..722062ec7 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/util/Util.java +++ b/src/main/java/org/whispersystems/textsecuregcm/util/Util.java @@ -21,6 +21,7 @@ import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Map; +import java.util.concurrent.TimeUnit; public class Util { @@ -119,4 +120,8 @@ public class Util { Thread.sleep(i); } catch (InterruptedException ie) {} } + + public static long todayInMillis() { + return TimeUnit.DAYS.toMillis(TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis())); + } } diff --git a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java index dbb8edbf8..eb48748b2 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java +++ b/src/main/java/org/whispersystems/textsecuregcm/websocket/AuthenticatedConnectListener.java @@ -9,6 +9,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.PubSubManager; import org.whispersystems.textsecuregcm.storage.StoredMessages; +import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.websocket.session.WebSocketSessionContext; import org.whispersystems.websocket.setup.WebSocketConnectListener; @@ -48,6 +49,11 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener { return; } + if (device.get().getLastSeen() != Util.todayInMillis()) { + device.get().setLastSeen(Util.todayInMillis()); + accountsManager.update(account.get()); + } + final WebSocketConnection connection = new WebSocketConnection(accountsManager, pushSender, storedMessages, pubSubManager, account.get(), device.get(), diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java index 0d8a60499..c4e84bad0 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/FederatedControllerTest.java @@ -27,8 +27,10 @@ import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import javax.ws.rs.core.MediaType; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import io.dropwizard.testing.junit.ResourceTestRule; import static org.hamcrest.CoreMatchers.equalTo; @@ -69,13 +71,13 @@ public class FederatedControllerTest { @Before public void setup() throws Exception { - List singleDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null)); + Set singleDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis())); }}; - List multiDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null)); - add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null)); + Set multiDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null, System.currentTimeMillis())); + add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null, System.currentTimeMillis())); }}; Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList); diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java index 436a10207..8f377a646 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java @@ -26,8 +26,10 @@ import org.whispersystems.textsecuregcm.storage.Keys; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import javax.ws.rs.core.MediaType; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import io.dropwizard.testing.junit.ResourceTestRule; import static org.fest.assertions.api.Assertions.assertThat; @@ -73,7 +75,7 @@ public class KeyControllerTest { final Device sampleDevice3 = mock(Device.class); final Device sampleDevice4 = mock(Device.class); - List allDevices = new LinkedList() {{ + Set allDevices = new HashSet() {{ add(sampleDevice); add(sampleDevice2); add(sampleDevice3); @@ -198,10 +200,10 @@ public class KeyControllerTest { .get(PreKeyResponseV2.class); assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey()); - assertThat(result.getDevices().size()).isEqualTo(1); - assertThat(result.getDevices().get(0).getPreKey().getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId()); - assertThat(result.getDevices().get(0).getPreKey().getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey()); - assertThat(result.getDevices().get(0).getSignedPreKey()).isEqualTo(existsAccount.getDevice(1).get().getSignedPreKey()); + assertThat(result.getDevicesCount()).isEqualTo(1); + assertThat(result.getDevice(1).getPreKey().getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId()); + assertThat(result.getDevice(1).getPreKey().getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey()); + assertThat(result.getDevice(1).getSignedPreKey()).isEqualTo(existsAccount.getDevice(1).get().getSignedPreKey()); verify(keys).get(eq(EXISTS_NUMBER), eq(1L)); verifyNoMoreInteractions(keys); @@ -245,13 +247,13 @@ public class KeyControllerTest { .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) .get(PreKeyResponseV2.class); - assertThat(results.getDevices().size()).isEqualTo(3); + assertThat(results.getDevicesCount()).isEqualTo(3); assertThat(results.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey()); - PreKeyV2 signedPreKey = results.getDevices().get(0).getSignedPreKey(); - PreKeyV2 preKey = results.getDevices().get(0).getPreKey(); - long registrationId = results.getDevices().get(0).getRegistrationId(); - long deviceId = results.getDevices().get(0).getDeviceId(); + PreKeyV2 signedPreKey = results.getDevice(1).getSignedPreKey(); + PreKeyV2 preKey = results.getDevice(1).getPreKey(); + long registrationId = results.getDevice(1).getRegistrationId(); + long deviceId = results.getDevice(1).getDeviceId(); assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY.getKeyId()); assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY.getPublicKey()); @@ -260,10 +262,10 @@ public class KeyControllerTest { assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY.getPublicKey()); assertThat(deviceId).isEqualTo(1); - signedPreKey = results.getDevices().get(1).getSignedPreKey(); - preKey = results.getDevices().get(1).getPreKey(); - registrationId = results.getDevices().get(1).getRegistrationId(); - deviceId = results.getDevices().get(1).getDeviceId(); + signedPreKey = results.getDevice(2).getSignedPreKey(); + preKey = results.getDevice(2).getPreKey(); + registrationId = results.getDevice(2).getRegistrationId(); + deviceId = results.getDevice(2).getDeviceId(); assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY2.getKeyId()); assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY2.getPublicKey()); @@ -272,10 +274,10 @@ public class KeyControllerTest { assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY2.getPublicKey()); assertThat(deviceId).isEqualTo(2); - signedPreKey = results.getDevices().get(2).getSignedPreKey(); - preKey = results.getDevices().get(2).getPreKey(); - registrationId = results.getDevices().get(2).getRegistrationId(); - deviceId = results.getDevices().get(2).getDeviceId(); + signedPreKey = results.getDevice(4).getSignedPreKey(); + preKey = results.getDevice(4).getPreKey(); + registrationId = results.getDevice(4).getRegistrationId(); + deviceId = results.getDevice(4).getDeviceId(); assertThat(preKey.getKeyId()).isEqualTo(SAMPLE_KEY4.getKeyId()); assertThat(preKey.getPublicKey()).isEqualTo(SAMPLE_KEY4.getPublicKey()); diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java index 290357d51..5d648510b 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java @@ -6,10 +6,13 @@ import com.sun.jersey.api.client.ClientResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.SignedPreKey; import org.whispersystems.textsecuregcm.entities.StaleDevices; import org.whispersystems.textsecuregcm.federation.FederatedClientManager; import org.whispersystems.textsecuregcm.limits.RateLimiter; @@ -21,8 +24,9 @@ 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 java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; import io.dropwizard.testing.junit.ResourceTestRule; import static org.hamcrest.CoreMatchers.equalTo; @@ -57,13 +61,14 @@ public class MessageControllerTest { @Before public void setup() throws Exception { - List singleDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null)); + Set singleDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis())); }}; - List multiDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null)); - add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null)); + Set multiDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis())); + add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis())); + add(new Device(3, "foo", "bar", "baz", "isgcm", null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31))); }}; Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList); diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ReceiptControllerTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ReceiptControllerTest.java index b199c0103..bb8fd5839 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ReceiptControllerTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ReceiptControllerTest.java @@ -18,8 +18,11 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; import io.dropwizard.testing.junit.ResourceTestRule; import static org.fest.assertions.api.Assertions.assertThat; @@ -47,13 +50,13 @@ public class ReceiptControllerTest { @Before public void setup() throws Exception { - List singleDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null)); + Set singleDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 111, null, System.currentTimeMillis())); }}; - List multiDeviceList = new LinkedList() {{ - add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null)); - add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null)); + Set multiDeviceList = new HashSet() {{ + add(new Device(1, "foo", "bar", "baz", "isgcm", null, false, 222, null, System.currentTimeMillis())); + add(new Device(2, "foo", "bar", "baz", "isgcm", null, false, 333, null, System.currentTimeMillis())); }}; Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, false, singleDeviceList); diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java index be925c948..65cd33ee6 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java @@ -25,8 +25,10 @@ import org.whispersystems.websocket.session.WebSocketSessionContext; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import io.dropwizard.auth.basic.BasicCredentials; import static org.junit.Assert.assertTrue; @@ -127,7 +129,7 @@ public class WebSocketConnectionTest { final Device sender1device = mock(Device.class); - List sender1devices = new LinkedList() {{ + Set sender1devices = new HashSet() {{ add(sender1device); }};