Device provisioning fixes.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-01-21 15:15:40 -08:00
parent f7132bdbbc
commit 45a0b74b89
13 changed files with 123 additions and 54 deletions

View File

@@ -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());

View File

@@ -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<Device> destinationDevices = destinationAccount.getDevices();
Account destinationAccount = getDestinationAccount(destination);
Set<Device> destinationDevices = destinationAccount.getDevices();
OutgoingMessageSignal.Builder message =
OutgoingMessageSignal.newBuilder()

View File

@@ -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<PreKeyResponseItemV2> 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();
}
}

View File

@@ -40,6 +40,6 @@ public class NonLimitedAccount extends Account {
@Override
public Optional<Device> 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()));
}
}

View File

@@ -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<Device> devices = new LinkedList<>();
private Set<Device> devices = new HashSet<>();
@JsonProperty
private String identityKey;
@@ -47,7 +47,7 @@ public class Account {
public Account() {}
@VisibleForTesting
public Account(String number, boolean supportsSms, List<Device> devices) {
public Account(String number, boolean supportsSms, Set<Device> 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<Device> devices) {
this.devices = devices;
}
public List<Device> getDevices() {
public Set<Device> 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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()));
}
}

View File

@@ -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(),