Support for video account attributes

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-01-09 11:50:56 -08:00
parent 5b28594189
commit 2dfe9eea94
17 changed files with 91 additions and 31 deletions

View File

@ -307,6 +307,7 @@ public class AccountController {
device.setName(attributes.getName());
device.setLastSeen(Util.todayInMillis());
device.setVoiceSupported(attributes.getVoice());
device.setVideoSupported(attributes.getVideo());
device.setRegistrationId(attributes.getRegistrationId());
device.setSignalingKey(attributes.getSignalingKey());
device.setUserAgent(userAgent);
@ -332,6 +333,7 @@ public class AccountController {
device.setRegistrationId(accountAttributes.getRegistrationId());
device.setName(accountAttributes.getName());
device.setVoiceSupported(accountAttributes.getVoice());
device.setVideoSupported(accountAttributes.getVideo());
device.setCreated(System.currentTimeMillis());
device.setLastSeen(Util.todayInMillis());
device.setUserAgent(userAgent);

View File

@ -150,7 +150,7 @@ public class FederationControllerV1 extends FederationController {
for (Account account : accountList) {
byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
if (!account.isActive()) {
clientContact.setInactive(true);

View File

@ -40,20 +40,24 @@ public class AccountAttributes {
@JsonProperty
private boolean voice;
@JsonProperty
private boolean video;
public AccountAttributes() {}
@VisibleForTesting
public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId) {
this(signalingKey, fetchesMessages, registrationId, null, false);
this(signalingKey, fetchesMessages, registrationId, null, false, false);
}
@VisibleForTesting
public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId, String name, boolean voice) {
public AccountAttributes(String signalingKey, boolean fetchesMessages, int registrationId, String name, boolean voice, boolean video) {
this.signalingKey = signalingKey;
this.fetchesMessages = fetchesMessages;
this.registrationId = registrationId;
this.name = name;
this.voice = voice;
this.video = video;
}
public String getSignalingKey() {
@ -76,4 +80,8 @@ public class AccountAttributes {
return voice;
}
public boolean getVideo() {
return video;
}
}

View File

@ -35,13 +35,17 @@ public class ClientContact {
@JsonProperty
private boolean voice;
@JsonProperty
private boolean video;
private String relay;
private boolean inactive;
public ClientContact(byte[] token, String relay, boolean voice) {
public ClientContact(byte[] token, String relay, boolean voice, boolean video) {
this.token = token;
this.relay = relay;
this.voice = voice;
this.video = video;
}
public ClientContact() {}
@ -74,6 +78,14 @@ public class ClientContact {
this.voice = voice;
}
public boolean isVideo() {
return video;
}
public void setVideo(boolean video) {
this.video = video;
}
@Override
public boolean equals(Object other) {
if (other == null) return false;
@ -85,6 +97,7 @@ public class ClientContact {
Arrays.equals(this.token, that.token) &&
this.inactive == that.inactive &&
this.voice == that.voice &&
this.video == that.video &&
(this.relay == null ? (that.relay == null) : this.relay.equals(that.relay));
}

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, null, null, false, 0, null, System.currentTimeMillis(), System.currentTimeMillis(), false, "NA"));
return Optional.of(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, System.currentTimeMillis(), System.currentTimeMillis(), false, false, "NA"));
}
}

View File

@ -72,7 +72,7 @@ public class Account {
}
public void removeDevice(long deviceId) {
this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, false, "NA"));
this.devices.remove(new Device(deviceId, null, null, null, null, null, null, null, false, 0, null, 0, 0, false, false, "NA"));
}
public Set<Device> getDevices() {
@ -103,6 +103,16 @@ public class Account {
return false;
}
public boolean isVideoSupported() {
for (Device device : devices) {
if (device.isActive() && device.isVideoSupported()) {
return true;
}
}
return false;
}
public boolean isActive() {
return
getMasterDevice().isPresent() &&

View File

@ -102,7 +102,7 @@ public class AccountsManager {
private void updateDirectory(Account account) {
if (account.isActive()) {
byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
directory.add(clientContact);
} else {
directory.remove(account.getNumber());

View File

@ -73,6 +73,9 @@ public class Device {
@JsonProperty
private boolean voice;
@JsonProperty
private boolean video;
@JsonProperty
private String userAgent;
@ -82,7 +85,7 @@ public class Device {
String signalingKey, String gcmId, String apnId,
String voipApnId, boolean fetchesMessages,
int registrationId, SignedPreKey signedPreKey,
long lastSeen, long created, boolean voice,
long lastSeen, long created, boolean voice, boolean video,
String userAgent)
{
this.id = id;
@ -99,6 +102,7 @@ public class Device {
this.lastSeen = lastSeen;
this.created = created;
this.voice = voice;
this.video = video;
this.userAgent = userAgent;
}
@ -174,6 +178,14 @@ public class Device {
this.voice = voice;
}
public boolean isVideoSupported() {
return video;
}
public void setVideoSupported(boolean video) {
this.video = video;
}
public void setAuthenticationCredentials(AuthenticationCredentials credentials) {
this.authToken = credentials.getHashedAuthenticationToken();
this.salt = credentials.getSalt();

View File

@ -72,7 +72,7 @@ public class DirectoryManager {
}
public void add(ClientContact contact) {
TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice());
TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice(), contact.isVideo());
try (Jedis jedis = redisPool.getResource()) {
jedis.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue));
@ -84,7 +84,7 @@ public class DirectoryManager {
public void add(BatchOperationHandle handle, ClientContact contact) {
try {
Pipeline pipeline = handle.pipeline;
TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice());
TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice(), contact.isVideo());
pipeline.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue));
} catch (JsonProcessingException e) {
@ -106,7 +106,7 @@ public class DirectoryManager {
}
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice));
return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice, tokenValue.video));
} catch (IOException e) {
logger.warn("JSON Error", e);
return Optional.absent();
@ -133,7 +133,7 @@ public class DirectoryManager {
try {
if (pair.second().get() != null) {
TokenValue tokenValue = objectMapper.readValue(pair.second().get(), TokenValue.class);
ClientContact clientContact = new ClientContact(pair.first(), tokenValue.relay, tokenValue.voice);
ClientContact clientContact = new ClientContact(pair.first(), tokenValue.relay, tokenValue.voice, tokenValue.video);
results.add(clientContact);
}
@ -178,11 +178,15 @@ public class DirectoryManager {
@JsonProperty(value = "v")
private boolean voice;
@JsonProperty(value = "w")
private boolean video;
public TokenValue() {}
public TokenValue(String relay, boolean voice) {
public TokenValue(String relay, boolean voice, boolean video) {
this.relay = relay;
this.voice = voice;
this.video = video;
}
}
@ -205,7 +209,7 @@ public class DirectoryManager {
}
TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class);
return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice));
return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice, tokenValue.video));
}
}

View File

@ -68,7 +68,7 @@ public class DirectoryUpdater {
for (Account account : accounts) {
if (account.isActive()) {
byte[] token = Util.getContactToken(account.getNumber());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported());
ClientContact clientContact = new ClientContact(token, null, account.isVoiceSupported(), account.isVideoSupported());
directory.add(batchOperation, clientContact);
contactsAdded++;

View File

@ -142,7 +142,7 @@ public class DeviceControllerTest {
.target("/v1/devices/5678901")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 1234, "this is a really long name that is longer than 80 characters", true),
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 1234, "this is a really long name that is longer than 80 characters", true, true),
MediaType.APPLICATION_JSON_TYPE));
assertEquals(response.getStatus(), 422);

View File

@ -79,12 +79,12 @@ public class FederatedControllerTest {
@Before
public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
}};
Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(222, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(333, "rad", "mad"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(222, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(333, "rad", "mad"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
}};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -74,13 +74,13 @@ public class MessageControllerTest {
@Before
public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
}};
Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), false, false, "Test"));
}};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -51,12 +51,12 @@ public class ReceiptControllerTest {
@Before
public void setup() throws Exception {
Set<Device> singleDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, null, System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
}};
Set<Device> multiDeviceList = new HashSet<Device>() {{
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis(), false, "Test"));
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, null, System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, null, System.currentTimeMillis(), System.currentTimeMillis(), false, false, "Test"));
}};
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList);

View File

@ -14,9 +14,10 @@ public class ClientContactTest {
@Test
public void serializeToJSON() throws Exception {
byte[] token = Util.getContactToken("+14152222222");
ClientContact contact = new ClientContact(token, null, false);
ClientContact contactWithRelay = new ClientContact(token, "whisper", false);
ClientContact contactWithRelayVox = new ClientContact(token, "whisper", true);
ClientContact contact = new ClientContact(token, null, false, false);
ClientContact contactWithRelay = new ClientContact(token, "whisper", false, false);
ClientContact contactWithRelayVox = new ClientContact(token, "whisper", true, false);
ClientContact contactWithRelayVid = new ClientContact(token, "whisper", true, true);
assertThat("Basic Contact Serialization works",
asJson(contact),
@ -29,12 +30,16 @@ public class ClientContactTest {
assertThat("Contact Relay Vox Serializaton works",
asJson(contactWithRelayVox),
is(equalTo(jsonFixture("fixtures/contact.relay.voice.json"))));
assertThat("Contact Relay Video Serializaton works",
asJson(contactWithRelayVid),
is(equalTo(jsonFixture("fixtures/contact.relay.video.json"))));
}
@Test
public void deserializeFromJSON() throws Exception {
ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"),
"whisper", false);
"whisper", false, false);
assertThat("a ClientContact can be deserialized from JSON",
fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class),

View File

@ -26,7 +26,7 @@ public class PreKeyTest {
@Test
public void deserializeFromJSONV() throws Exception {
ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"),
"whisper", false);
"whisper", false, false);
assertThat("a ClientContact can be deserialized from JSON",
fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class),

View File

@ -0,0 +1,6 @@
{
"token" : "BQVVHxMt5zAFXA",
"voice" : true,
"video" : true,
"relay" : "whisper"
}