chunkAccounts;
- if (fromNumber.isPresent()) {
- chunkAccounts = accounts.getAllFrom(fromNumber.get(), chunkSize);
+ if (fromUuid.isPresent()) {
+ chunkAccounts = accounts.getAllFrom(fromUuid.get(), chunkSize);
} else {
chunkAccounts = accounts.getAllFrom(chunkSize);
}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerCache.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerCache.java
index 73e977c16..4b1402b1a 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerCache.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerCache.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -18,17 +18,20 @@ package org.whispersystems.textsecuregcm.storage;
import org.whispersystems.textsecuregcm.redis.LuaScript;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
-import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
+import redis.clients.jedis.Jedis;
+
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class AccountDatabaseCrawlerCache {
private static final String ACTIVE_WORKER_KEY = "account_database_crawler_cache_active_worker";
- private static final String LAST_NUMBER_KEY = "account_database_crawler_cache_last_number";
+ private static final String LAST_UUID_KEY = "account_database_crawler_cache_last_uuid";
private static final String ACCELERATE_KEY = "account_database_crawler_cache_accelerate";
private static final long LAST_NUMBER_TTL_MS = 86400_000L;
@@ -65,18 +68,21 @@ public class AccountDatabaseCrawlerCache {
luaScript.execute(keys, args);
}
- public Optional getLastNumber() {
+ public Optional getLastUuid() {
try (Jedis jedis = jedisPool.getWriteResource()) {
- return Optional.ofNullable(jedis.get(LAST_NUMBER_KEY));
+ String lastUuidString = jedis.get(LAST_UUID_KEY);
+
+ if (lastUuidString == null) return Optional.empty();
+ else return Optional.of(UUID.fromString(lastUuidString));
}
}
- public void setLastNumber(Optional lastNumber) {
+ public void setLastUuid(Optional lastUuid) {
try (Jedis jedis = jedisPool.getWriteResource()) {
- if (lastNumber.isPresent()) {
- jedis.psetex(LAST_NUMBER_KEY, LAST_NUMBER_TTL_MS, lastNumber.get());
+ if (lastUuid.isPresent()) {
+ jedis.psetex(LAST_UUID_KEY, LAST_NUMBER_TTL_MS, lastUuid.get().toString());
} else {
- jedis.del(LAST_NUMBER_KEY);
+ jedis.del(LAST_UUID_KEY);
}
}
}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerListener.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerListener.java
index 051fc6638..590f61c38 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerListener.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountDatabaseCrawlerListener.java
@@ -18,9 +18,11 @@ package org.whispersystems.textsecuregcm.storage;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public interface AccountDatabaseCrawlerListener {
void onCrawlStart();
- void onCrawlChunk(Optional fromNumber, List chunkAccounts) throws AccountDatabaseCrawlerRestartException;
- void onCrawlEnd(Optional fromNumber);
+ void onCrawlChunk(Optional fromUuid, List chunkAccounts) throws AccountDatabaseCrawlerRestartException;
+ void onCrawlEnd(Optional fromUuid);
}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Accounts.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Accounts.java
index a0d0fe636..89a5a6417 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Accounts.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Accounts.java
@@ -28,24 +28,27 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name;
public class Accounts {
public static final String ID = "id";
+ public static final String UID = "uuid";
public static final String NUMBER = "number";
public static final String DATA = "data";
private static final ObjectMapper mapper = SystemMapper.getMapper();
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
- private final Timer createTimer = metricRegistry.timer(name(Accounts.class, "create"));
- private final Timer updateTimer = metricRegistry.timer(name(Accounts.class, "update"));
- private final Timer getTimer = metricRegistry.timer(name(Accounts.class, "get"));
- private final Timer getAllFromTimer = metricRegistry.timer(name(Accounts.class, "getAllFrom"));
+ private final Timer createTimer = metricRegistry.timer(name(Accounts.class, "create" ));
+ private final Timer updateTimer = metricRegistry.timer(name(Accounts.class, "update" ));
+ private final Timer getByNumberTimer = metricRegistry.timer(name(Accounts.class, "getByNumber" ));
+ private final Timer getByUuidTimer = metricRegistry.timer(name(Accounts.class, "getByUuid" ));
+ private final Timer getAllFromTimer = metricRegistry.timer(name(Accounts.class, "getAllFrom" ));
private final Timer getAllFromOffsetTimer = metricRegistry.timer(name(Accounts.class, "getAllFromOffset"));
- private final Timer vacuumTimer = metricRegistry.timer(name(Accounts.class, "vacuum"));
+ private final Timer vacuumTimer = metricRegistry.timer(name(Accounts.class, "vacuum" ));
private final FaultTolerantDatabase database;
@@ -57,16 +60,15 @@ public class Accounts {
public boolean create(Account account) {
return database.with(jdbi -> jdbi.inTransaction(TransactionIsolationLevel.SERIALIZABLE, handle -> {
try (Timer.Context ignored = createTimer.time()) {
- int rows = handle.createUpdate("DELETE FROM accounts WHERE " + NUMBER + " = :number")
- .bind("number", account.getNumber())
- .execute();
+ UUID uuid = handle.createQuery("INSERT INTO accounts (" + NUMBER + ", " + UID + ", " + DATA + ") VALUES (:number, :uuid, CAST(:data AS json)) ON CONFLICT(number) DO UPDATE SET data = EXCLUDED.data RETURNING uuid")
+ .bind("number", account.getNumber())
+ .bind("uuid", account.getUuid())
+ .bind("data", mapper.writeValueAsString(account))
+ .mapTo(UUID.class)
+ .findOnly();
- handle.createUpdate("INSERT INTO accounts (" + NUMBER + ", " + DATA + ") VALUES (:number, CAST(:data AS json))")
- .bind("number", account.getNumber())
- .bind("data", mapper.writeValueAsString(account))
- .execute();
-
- return rows == 0;
+ account.setUuid(uuid);
+ return uuid.equals(account.getUuid());
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
@@ -76,8 +78,8 @@ public class Accounts {
public void update(Account account) {
database.use(jdbi -> jdbi.useHandle(handle -> {
try (Timer.Context ignored = updateTimer.time()) {
- handle.createUpdate("UPDATE accounts SET " + DATA + " = CAST(:data AS json) WHERE " + NUMBER + " = :number")
- .bind("number", account.getNumber())
+ handle.createUpdate("UPDATE accounts SET " + DATA + " = CAST(:data AS json) WHERE " + UID + " = :uuid")
+ .bind("uuid", account.getUuid())
.bind("data", mapper.writeValueAsString(account))
.execute();
} catch (JsonProcessingException e) {
@@ -88,7 +90,7 @@ public class Accounts {
public Optional get(String number) {
return database.with(jdbi -> jdbi.withHandle(handle -> {
- try (Timer.Context ignored = getTimer.time()) {
+ try (Timer.Context ignored = getByNumberTimer.time()) {
return handle.createQuery("SELECT * FROM accounts WHERE " + NUMBER + " = :number")
.bind("number", number)
.mapTo(Account.class)
@@ -97,10 +99,21 @@ public class Accounts {
}));
}
- public List getAllFrom(String from, int length) {
+ public Optional get(UUID uuid) {
+ return database.with(jdbi -> jdbi.withHandle(handle -> {
+ try (Timer.Context ignored = getByUuidTimer.time()) {
+ return handle.createQuery("SELECT * FROM accounts WHERE " + UID + " = :uuid")
+ .bind("uuid", uuid)
+ .mapTo(Account.class)
+ .findFirst();
+ }
+ }));
+ }
+
+ public List getAllFrom(UUID from, int length) {
return database.with(jdbi -> jdbi.withHandle(handle -> {
try (Timer.Context ignored = getAllFromOffsetTimer.time()) {
- return handle.createQuery("SELECT * FROM accounts WHERE " + NUMBER + " > :from ORDER BY " + NUMBER + " LIMIT :limit")
+ return handle.createQuery("SELECT * FROM accounts WHERE " + UID + " > :from ORDER BY " + UID + " LIMIT :limit")
.bind("from", from)
.bind("limit", length)
.mapTo(Account.class)
@@ -112,7 +125,7 @@ public class Accounts {
public List getAllFrom(int length) {
return database.with(jdbi -> jdbi.withHandle(handle -> {
try (Timer.Context ignored = getAllFromTimer.time()) {
- return handle.createQuery("SELECT * FROM accounts ORDER BY " + NUMBER + " LIMIT :limit")
+ return handle.createQuery("SELECT * FROM accounts ORDER BY " + UID + " LIMIT :limit")
.bind("limit", length)
.mapTo(Account.class)
.list();
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
index 0697e47cb..38b07866d 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.entities.ClientContact;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.Constants;
@@ -33,6 +34,7 @@ import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name;
import redis.clients.jedis.Jedis;
@@ -40,13 +42,15 @@ import redis.clients.jedis.exceptions.JedisException;
public class AccountsManager {
- private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
- private static final Timer createTimer = metricRegistry.timer(name(AccountsManager.class, "create" ));
- private static final Timer updateTimer = metricRegistry.timer(name(AccountsManager.class, "update" ));
- private static final Timer getTimer = metricRegistry.timer(name(AccountsManager.class, "get" ));
+ private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
+ private static final Timer createTimer = metricRegistry.timer(name(AccountsManager.class, "create" ));
+ private static final Timer updateTimer = metricRegistry.timer(name(AccountsManager.class, "update" ));
+ private static final Timer getByNumberTimer = metricRegistry.timer(name(AccountsManager.class, "getByNumber"));
+ private static final Timer getByUuidTimer = metricRegistry.timer(name(AccountsManager.class, "getByUuid" ));
- private static final Timer redisSetTimer = metricRegistry.timer(name(AccountsManager.class, "redisSet" ));
- private static final Timer redisGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisGet" ));
+ private static final Timer redisSetTimer = metricRegistry.timer(name(AccountsManager.class, "redisSet" ));
+ private static final Timer redisNumberGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisNumberGet"));
+ private static final Timer redisUuidGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisUuidGet" ));
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
@@ -65,7 +69,7 @@ public class AccountsManager {
public boolean create(Account account) {
try (Timer.Context context = createTimer.time()) {
boolean freshUser = databaseCreate(account);
- redisSet(account.getNumber(), account, false);
+ redisSet(account);
updateDirectory(account);
return freshUser;
@@ -74,31 +78,51 @@ public class AccountsManager {
public void update(Account account) {
try (Timer.Context context = updateTimer.time()) {
- redisSet(account.getNumber(), account, false);
+ redisSet(account);
databaseUpdate(account);
updateDirectory(account);
}
}
+ public Optional get(AmbiguousIdentifier identifier) {
+ if (identifier.hasNumber()) return get(identifier.getNumber());
+ else if (identifier.hasUuid()) return get(identifier.getUuid());
+ else throw new AssertionError();
+ }
+
public Optional get(String number) {
- try (Timer.Context context = getTimer.time()) {
+ try (Timer.Context context = getByNumberTimer.time()) {
Optional account = redisGet(number);
if (!account.isPresent()) {
account = databaseGet(number);
- account.ifPresent(value -> redisSet(number, value, true));
+ account.ifPresent(value -> redisSet(value));
}
return account;
}
}
+ public Optional get(UUID uuid) {
+ try (Timer.Context context = getByUuidTimer.time()) {
+ Optional account = redisGet(uuid);
+
+ if (!account.isPresent()) {
+ account = databaseGet(uuid);
+ account.ifPresent(value -> redisSet(value));
+ }
+
+ return account;
+ }
+ }
+
+
public List getAllFrom(int length) {
return accounts.getAllFrom(length);
}
- public List getAllFrom(String number, int length) {
- return accounts.getAllFrom(number, length);
+ public List getAllFrom(UUID uuid, int length) {
+ return accounts.getAllFrom(uuid, length);
}
private void updateDirectory(Account account) {
@@ -111,15 +135,20 @@ public class AccountsManager {
}
}
- private String getKey(String number) {
- return Account.class.getSimpleName() + Account.MEMCACHE_VERION + number;
+ private String getAccountMapKey(String number) {
+ return "AccountMap::" + number;
}
- private void redisSet(String number, Account account, boolean optional) {
+ private String getAccountEntityKey(UUID uuid) {
+ return "Account::" + uuid.toString();
+ }
+
+ private void redisSet(Account account) {
try (Jedis jedis = cacheClient.getWriteResource();
Timer.Context timer = redisSetTimer.time())
{
- jedis.set(getKey(number), mapper.writeValueAsString(account));
+ jedis.set(getAccountMapKey(account.getNumber()), account.getUuid().toString());
+ jedis.set(getAccountEntityKey(account.getUuid()), mapper.writeValueAsString(account));
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}
@@ -127,20 +156,14 @@ public class AccountsManager {
private Optional redisGet(String number) {
try (Jedis jedis = cacheClient.getReadResource();
- Timer.Context timer = redisGetTimer.time())
+ Timer.Context timer = redisNumberGetTimer.time())
{
- String json = jedis.get(getKey(number));
+ String uuid = jedis.get(getAccountMapKey(number));
- if (json != null) {
- Account account = mapper.readValue(json, Account.class);
- account.setNumber(number);
-
- return Optional.of(account);
- }
-
- return Optional.empty();
- } catch (IOException e) {
- logger.warn("AccountsManager", "Deserialization error", e);
+ if (uuid != null) return redisGet(UUID.fromString(uuid));
+ else return Optional.empty();
+ } catch (IllegalArgumentException e) {
+ logger.warn("Deserialization error", e);
return Optional.empty();
} catch (JedisException e) {
logger.warn("Redis failure", e);
@@ -148,10 +171,38 @@ public class AccountsManager {
}
}
+ private Optional redisGet(UUID uuid) {
+ try (Jedis jedis = cacheClient.getReadResource();
+ Timer.Context timer = redisUuidGetTimer.time())
+ {
+ String json = jedis.get(getAccountEntityKey(uuid));
+
+ if (json != null) {
+ Account account = mapper.readValue(json, Account.class);
+ account.setUuid(uuid);
+
+ return Optional.of(account);
+ }
+
+ return Optional.empty();
+ } catch (IOException e) {
+ logger.warn("Deserialization error", e);
+ return Optional.empty();
+ } catch (JedisException e) {
+ logger.warn("Redis failure", e);
+ return Optional.empty();
+ }
+
+ }
+
private Optional databaseGet(String number) {
return accounts.get(number);
}
+ private Optional databaseGet(UUID uuid) {
+ return accounts.get(uuid);
+ }
+
private boolean databaseCreate(Account account) {
return accounts.create(account);
}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/ActiveUserCounter.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/ActiveUserCounter.java
index 8ef5cb6c6..099cae5d5 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/ActiveUserCounter.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/ActiveUserCounter.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -20,22 +20,22 @@ import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.dropwizard.metrics.MetricsFactory;
-import io.dropwizard.metrics.ReporterFactory;
import org.whispersystems.textsecuregcm.entities.ActiveUserTally;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.Util;
-import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
+import io.dropwizard.metrics.MetricsFactory;
+import io.dropwizard.metrics.ReporterFactory;
+import redis.clients.jedis.Jedis;
public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
@@ -44,8 +44,6 @@ public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
private static final String PLATFORM_IOS = "ios";
private static final String PLATFORM_ANDROID = "android";
- private static final String FIRST_FROM_NUMBER = "+";
-
private static final String INTERVALS[] = {"daily", "weekly", "monthly", "quarterly", "yearly"};
private final MetricsFactory metricsFactory;
@@ -64,7 +62,8 @@ public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
}
}
- public void onCrawlChunk(Optional fromNumber, List chunkAccounts) {
+ @Override
+ public void onCrawlChunk(Optional fromNumber, List chunkAccounts) {
long nowDays = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis());
long agoMs[] = {TimeUnit.DAYS.toMillis(nowDays - 1),
TimeUnit.DAYS.toMillis(nowDays - 7),
@@ -107,47 +106,40 @@ public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
}
}
- incrementTallies(fromNumber.orElse(FIRST_FROM_NUMBER), platformIncrements, countryIncrements);
-
+ incrementTallies(fromNumber.orElse(UUID.randomUUID()), platformIncrements, countryIncrements);
}
- public void onCrawlEnd(Optional fromNumber) {
- MetricRegistry metrics = new MetricRegistry();
- long intervalTallies[] = new long[INTERVALS.length];
- ActiveUserTally activeUserTally = getFinalTallies();
- Map platforms = activeUserTally.getPlatforms();
+ @Override
+ public void onCrawlEnd(Optional fromNumber) {
+ MetricRegistry metrics = new MetricRegistry();
+ long intervalTallies[] = new long[INTERVALS.length];
+ ActiveUserTally activeUserTally = getFinalTallies();
+ Map platforms = activeUserTally.getPlatforms();
+
platforms.forEach((platform, platformTallies) -> {
- for (int i = 0; i < INTERVALS.length; i++) {
- final long tally = platformTallies[i];
- metrics.register(metricKey(platform, INTERVALS[i]),
- new Gauge() {
- @Override
- public Long getValue() { return tally; }
- });
- intervalTallies[i] += tally;
- }
- });
+ for (int i = 0; i < INTERVALS.length; i++) {
+ final long tally = platformTallies[i];
+ metrics.register(metricKey(platform, INTERVALS[i]),
+ (Gauge) () -> tally);
+ intervalTallies[i] += tally;
+ }
+ });
Map countries = activeUserTally.getCountries();
countries.forEach((country, countryTallies) -> {
- for (int i = 0; i < INTERVALS.length; i++) {
- final long tally = countryTallies[i];
- metrics.register(metricKey(country, INTERVALS[i]),
- new Gauge() {
- @Override
- public Long getValue() { return tally; }
- });
- }
- });
+ for (int i = 0; i < INTERVALS.length; i++) {
+ final long tally = countryTallies[i];
+ metrics.register(metricKey(country, INTERVALS[i]),
+ (Gauge) () -> tally);
+ }
+ });
for (int i = 0; i < INTERVALS.length; i++) {
final long intervalTotal = intervalTallies[i];
metrics.register(metricKey(INTERVALS[i]),
- new Gauge() {
- @Override
- public Long getValue() { return intervalTotal; }
- });
+ (Gauge) () -> intervalTotal);
}
+
for (ReporterFactory reporterFactory : metricsFactory.getReporters()) {
reporterFactory.build(metrics).report();
}
@@ -162,22 +154,25 @@ public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
return tally;
}
- private void incrementTallies(String fromNumber, Map platformIncrements, Map countryIncrements) {
+ private void incrementTallies(UUID fromUuid, Map platformIncrements, Map countryIncrements) {
try (Jedis jedis = jedisPool.getWriteResource()) {
String tallyValue = jedis.get(TALLY_KEY);
ActiveUserTally activeUserTally;
+
if (tallyValue == null) {
- activeUserTally = new ActiveUserTally(fromNumber, platformIncrements, countryIncrements);
+ activeUserTally = new ActiveUserTally(fromUuid, platformIncrements, countryIncrements);
} else {
activeUserTally = mapper.readValue(tallyValue, ActiveUserTally.class);
- if (activeUserTally.getFromNumber() != fromNumber) {
- activeUserTally.setFromNumber(fromNumber);
+
+ if (!fromUuid.equals(activeUserTally.getFromUuid())) {
+ activeUserTally.setFromUuid(fromUuid);
Map platformTallies = activeUserTally.getPlatforms();
addTallyMaps(platformTallies, platformIncrements);
Map countryTallies = activeUserTally.getCountries();
addTallyMaps(countryTallies, countryIncrements);
}
}
+
jedis.set(TALLY_KEY, mapper.writeValueAsString(activeUserTally));
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
@@ -188,15 +183,15 @@ public class ActiveUserCounter implements AccountDatabaseCrawlerListener {
private void addTallyMaps(Map tallyMap, Map incrementMap) {
incrementMap.forEach((key, increments) -> {
- long[] tallies = tallyMap.get(key);
- if (tallies == null) {
- tallyMap.put(key, increments);
- } else {
- for (int i = 0; i < INTERVALS.length; i++) {
- tallies[i] += increments[i];
- }
+ long[] tallies = tallyMap.get(key);
+ if (tallies == null) {
+ tallyMap.put(key, increments);
+ } else {
+ for (int i = 0; i < INTERVALS.length; i++) {
+ tallies[i] += increments[i];
}
- });
+ }
+ });
}
private ActiveUserTally getFinalTallies() {
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
index 972e9230f..f9718f02b 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java
@@ -33,6 +33,7 @@ import javax.ws.rs.ProcessingException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import java.util.stream.Collectors;
import static com.codahale.metrics.MetricRegistry.name;
@@ -55,18 +56,16 @@ public class DirectoryReconciler implements AccountDatabaseCrawlerListener {
public void onCrawlStart() { }
- public void onCrawlEnd(Optional fromNumber) {
-
- DirectoryReconciliationRequest request = new DirectoryReconciliationRequest(fromNumber.orElse(null), null, Collections.emptyList());
+ public void onCrawlEnd(Optional fromUuid) {
+ DirectoryReconciliationRequest request = new DirectoryReconciliationRequest(fromUuid.orElse(null), null, Collections.emptyList());
DirectoryReconciliationResponse response = sendChunk(request);
-
}
- public void onCrawlChunk(Optional fromNumber, List chunkAccounts) throws AccountDatabaseCrawlerRestartException {
+ public void onCrawlChunk(Optional fromUuid, List chunkAccounts) throws AccountDatabaseCrawlerRestartException {
updateDirectoryCache(chunkAccounts);
- DirectoryReconciliationRequest request = createChunkRequest(fromNumber, chunkAccounts);
+ DirectoryReconciliationRequest request = createChunkRequest(fromUuid, chunkAccounts);
DirectoryReconciliationResponse response = sendChunk(request);
if (response.getStatus() == DirectoryReconciliationResponse.Status.MISSING) {
throw new AccountDatabaseCrawlerRestartException("directory reconciler missing");
@@ -93,19 +92,19 @@ public class DirectoryReconciler implements AccountDatabaseCrawlerListener {
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- private DirectoryReconciliationRequest createChunkRequest(Optional fromNumber, List accounts) {
+ private DirectoryReconciliationRequest createChunkRequest(Optional fromUuid, List accounts) {
List numbers = accounts.stream()
.filter(Account::isEnabled)
.map(Account::getNumber)
.collect(Collectors.toList());
- Optional toNumber = Optional.empty();
+ Optional toUuid = Optional.empty();
if (!accounts.isEmpty()) {
- toNumber = Optional.of(accounts.get(accounts.size() - 1).getNumber());
+ toUuid = Optional.of(accounts.get(accounts.size() - 1).getUuid());
}
- return new DirectoryReconciliationRequest(fromNumber.orElse(null), toNumber.orElse(null), numbers);
+ return new DirectoryReconciliationRequest(fromUuid.orElse(null), toUuid.orElse(null), numbers);
}
private DirectoryReconciliationResponse sendChunk(DirectoryReconciliationRequest request) {
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
index 7af026dc0..a1acbd2c8 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Messages.java
@@ -25,6 +25,7 @@ public class Messages {
public static final String TIMESTAMP = "timestamp";
public static final String SERVER_TIMESTAMP = "server_timestamp";
public static final String SOURCE = "source";
+ public static final String SOURCE_UUID = "source_uuid";
public static final String SOURCE_DEVICE = "source_device";
public static final String DESTINATION = "destination";
public static final String DESTINATION_DEVICE = "destination_device";
@@ -51,8 +52,8 @@ public class Messages {
public void store(UUID guid, Envelope message, String destination, long destinationDevice) {
database.use(jdbi ->jdbi.useHandle(handle -> {
try (Timer.Context ignored = storeTimer.time()) {
- handle.createUpdate("INSERT INTO messages (" + GUID + ", " + TYPE + ", " + RELAY + ", " + TIMESTAMP + ", " + SERVER_TIMESTAMP + ", " + SOURCE + ", " + SOURCE_DEVICE + ", " + DESTINATION + ", " + DESTINATION_DEVICE + ", " + MESSAGE + ", " + CONTENT + ") " +
- "VALUES (:guid, :type, :relay, :timestamp, :server_timestamp, :source, :source_device, :destination, :destination_device, :message, :content)")
+ handle.createUpdate("INSERT INTO messages (" + GUID + ", " + TYPE + ", " + RELAY + ", " + TIMESTAMP + ", " + SERVER_TIMESTAMP + ", " + SOURCE + ", " + SOURCE_UUID + ", " + SOURCE_DEVICE + ", " + DESTINATION + ", " + DESTINATION_DEVICE + ", " + MESSAGE + ", " + CONTENT + ") " +
+ "VALUES (:guid, :type, :relay, :timestamp, :server_timestamp, :source, :source_uuid, :source_device, :destination, :destination_device, :message, :content)")
.bind("guid", guid)
.bind("destination", destination)
.bind("destination_device", destinationDevice)
@@ -61,6 +62,7 @@ public class Messages {
.bind("timestamp", message.getTimestamp())
.bind("server_timestamp", message.getServerTimestamp())
.bind("source", message.hasSource() ? message.getSource() : null)
+ .bind("source_uuid", message.hasSourceUuid() ? UUID.fromString(message.getSourceUuid()) : null)
.bind("source_device", message.hasSourceDevice() ? message.getSourceDevice() : null)
.bind("message", message.hasLegacyMessage() ? message.getLegacyMessage().toByteArray() : null)
.bind("content", message.hasContent() ? message.getContent().toByteArray() : null)
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
index aef6cbf2b..0a3d2a8df 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/MessagesCache.java
@@ -203,6 +203,7 @@ public class MessagesCache implements Managed {
envelope.getRelay(),
envelope.getTimestamp(),
envelope.getSource(),
+ envelope.hasSourceUuid() ? UUID.fromString(envelope.getSourceUuid()) : null,
envelope.getSourceDevice(),
envelope.hasLegacyMessage() ? envelope.getLegacyMessage().toByteArray() : null,
envelope.hasContent() ? envelope.getContent().toByteArray() : null,
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PublicAccount.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/PublicAccount.java
deleted file mode 100644
index 83514e408..000000000
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PublicAccount.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.whispersystems.textsecuregcm.storage;
-
-public class PublicAccount extends Account {
-
- public PublicAccount() {}
-
- public PublicAccount(Account account) {
- setIdentityKey(account.getIdentityKey());
- setUnidentifiedAccessKey(account.getUnidentifiedAccessKey().orElse(null));
- setUnrestrictedUnidentifiedAccess(account.isUnrestrictedUnidentifiedAccess());
- setAvatar(account.getAvatar());
- setProfileName(account.getProfileName());
- setPin("******");
-
- account.getDevices().forEach(this::addDevice);
- }
-
-}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java
index 25e21083b..c656f1c95 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java
@@ -9,6 +9,7 @@ import org.whispersystems.textsecuregcm.util.Util;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.codahale.metrics.MetricRegistry.name;
@@ -31,7 +32,7 @@ public class PushFeedbackProcessor implements AccountDatabaseCrawlerListener {
public void onCrawlStart() {}
@Override
- public void onCrawlChunk(Optional fromNumber, List chunkAccounts) {
+ public void onCrawlChunk(Optional fromUuid, List chunkAccounts) {
for (Account account : chunkAccounts) {
boolean update = false;
@@ -65,5 +66,5 @@ public class PushFeedbackProcessor implements AccountDatabaseCrawlerListener {
}
@Override
- public void onCrawlEnd(Optional fromNumber) {}
+ public void onCrawlEnd(Optional toUuid) {}
}
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/AccountRowMapper.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/AccountRowMapper.java
index 7b608b1ac..be504999e 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/AccountRowMapper.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/AccountRowMapper.java
@@ -10,6 +10,7 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.UUID;
public class AccountRowMapper implements RowMapper {
@@ -20,6 +21,7 @@ public class AccountRowMapper implements RowMapper {
try {
Account account = mapper.readValue(resultSet.getString(Accounts.DATA), Account.class);
account.setNumber(resultSet.getString(Accounts.NUMBER));
+ account.setUuid(UUID.fromString(resultSet.getString(Accounts.UID)));
return account;
} catch (IOException e) {
throw new SQLException(e);
diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/OutgoingMessageEntityRowMapper.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/OutgoingMessageEntityRowMapper.java
index 2f238ea98..3bea1b217 100644
--- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/OutgoingMessageEntityRowMapper.java
+++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/OutgoingMessageEntityRowMapper.java
@@ -16,6 +16,7 @@ public class OutgoingMessageEntityRowMapper implements RowMapper
+
+
+
+
+
+
+
+ CREATE UNIQUE INDEX CONCURRENTLY uuid_index ON accounts (uuid);
+
+
diff --git a/service/src/main/resources/messagedb.xml b/service/src/main/resources/messagedb.xml
index eacbe4611..fc90a278e 100644
--- a/service/src/main/resources/messagedb.xml
+++ b/service/src/main/resources/messagedb.xml
@@ -121,4 +121,10 @@
CREATE INDEX CONCURRENTLY guid_index ON messages (guid);
+
+
+
+
+
+
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
index c1401608a..18368b503 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java
@@ -14,9 +14,10 @@ import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
import org.whispersystems.textsecuregcm.controllers.AccountController;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
+import org.whispersystems.textsecuregcm.entities.AccountCreationResult;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
-import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
import org.whispersystems.textsecuregcm.entities.DeprecatedPin;
+import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
import org.whispersystems.textsecuregcm.entities.RegistrationLock;
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
@@ -463,15 +464,15 @@ public class AccountControllerTest {
@Test
public void testVerifyCode() throws Exception {
- Response response =
+ AccountCreationResult result =
resources.getJerseyTest()
.target(String.format("/v1/accounts/code/%s", "1234"))
.request()
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 2222, null),
- MediaType.APPLICATION_JSON_TYPE));
+ MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
- assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(result.getUuid()).isNotNull();
verify(accountsManager, times(1)).create(isA(Account.class));
verify(directoryQueue, times(1)).deleteRegisteredUser(eq(SENDER));
@@ -509,30 +510,30 @@ public class AccountControllerTest {
@Test
public void testVerifyPin() throws Exception {
- Response response =
+ AccountCreationResult result =
resources.getJerseyTest()
.target(String.format("/v1/accounts/code/%s", "333333"))
.request()
.header("Authorization", AuthHelper.getAuthHeader(SENDER_PIN, "bar"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 3333, "31337"),
- MediaType.APPLICATION_JSON_TYPE));
+ MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
- assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(result.getUuid()).isNotNull();
verify(pinLimiter).validate(eq(SENDER_PIN));
}
@Test
public void testVerifyRegistrationLock() throws Exception {
- Response response =
+ AccountCreationResult result =
resources.getJerseyTest()
.target(String.format("/v1/accounts/code/%s", "666666"))
.request()
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 3333, null, null, Hex.toStringCondensed(registration_lock_key)),
- MediaType.APPLICATION_JSON_TYPE));
+ MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
- assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(result.getUuid()).isNotNull();
verify(pinLimiter).validate(eq(SENDER_REG_LOCK));
}
@@ -628,15 +629,15 @@ public class AccountControllerTest {
try {
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7));
- Response response =
+ AccountCreationResult result =
resources.getJerseyTest()
.target(String.format("/v1/accounts/code/%s", "444444"))
.request()
.header("Authorization", AuthHelper.getAuthHeader(SENDER_OVER_PIN, "bar"))
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 3333, null),
- MediaType.APPLICATION_JSON_TYPE));
+ MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
- assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(result.getUuid()).isNotNull();
} finally {
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
@@ -666,7 +667,7 @@ public class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/registration_lock/")
.request()
- .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
.put(Entity.json(new RegistrationLock("1234567890123456789012345678901234567890123456789012345678901234")));
assertThat(response.getStatus()).isEqualTo(204);
@@ -745,7 +746,6 @@ public class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(401);
}
-
@Test
public void testSetGcmId() throws Exception {
Response response =
@@ -761,6 +761,21 @@ public class AccountControllerTest {
verify(accountsManager, times(1)).update(eq(AuthHelper.DISABLED_ACCOUNT));
}
+ @Test
+ public void testSetGcmIdByUuid() throws Exception {
+ Response response =
+ resources.getJerseyTest()
+ .target("/v1/accounts/gcm/")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID.toString(), AuthHelper.DISABLED_PASSWORD))
+ .put(Entity.json(new GcmRegistrationId("z000")));
+
+ assertThat(response.getStatus()).isEqualTo(204);
+
+ verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("z000"));
+ verify(accountsManager, times(1)).update(eq(AuthHelper.DISABLED_ACCOUNT));
+ }
+
@Test
public void testSetApnId() throws Exception {
Response response =
@@ -777,5 +792,32 @@ public class AccountControllerTest {
verify(accountsManager, times(1)).update(eq(AuthHelper.DISABLED_ACCOUNT));
}
+ @Test
+ public void testSetApnIdByUuid() throws Exception {
+ Response response =
+ resources.getJerseyTest()
+ .target("/v1/accounts/apn/")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID.toString(), AuthHelper.DISABLED_PASSWORD))
+ .put(Entity.json(new ApnRegistrationId("third", "fourth")));
+
+ assertThat(response.getStatus()).isEqualTo(204);
+
+ verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("third"));
+ verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("fourth"));
+ verify(accountsManager, times(1)).update(eq(AuthHelper.DISABLED_ACCOUNT));
+ }
+
+ @Test
+ public void testWhoAmI() {
+ AccountCreationResult response =
+ resources.getJerseyTest()
+ .target("/v1/accounts/whoami/")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
+ .get(AccountCreationResult.class);
+
+ assertThat(response.getUuid()).isEqualTo(AuthHelper.VALID_UUID);
+ }
}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java
index e786fb8a5..81239758d 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java
@@ -75,6 +75,7 @@ public class CertificateControllerTest {
assertEquals(certificate.getSender(), AuthHelper.VALID_NUMBER);
assertEquals(certificate.getSenderDevice(), 1L);
+ assertEquals(certificate.getSenderUuid(), AuthHelper.VALID_UUID.toString());
assertTrue(Arrays.equals(certificate.getIdentityKey().toByteArray(), Base64.decode(AuthHelper.VALID_IDENTITY)));
}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
index 3eb0b4be7..791a72626 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeyControllerTest.java
@@ -6,6 +6,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.controllers.KeysController;
@@ -32,6 +34,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule;
@@ -40,8 +43,11 @@ import static org.mockito.Mockito.*;
public class KeyControllerTest {
- private static final String EXISTS_NUMBER = "+14152222222";
+ private static final String EXISTS_NUMBER = "+14152222222";
+ private static final UUID EXISTS_UUID = UUID.randomUUID();
+
private static String NOT_EXISTS_NUMBER = "+14152222220";
+ private static UUID NOT_EXISTS_UUID = UUID.randomUUID();
private static int SAMPLE_REGISTRATION_ID = 999;
private static int SAMPLE_REGISTRATION_ID2 = 1002;
@@ -117,7 +123,14 @@ public class KeyControllerTest {
when(existsAccount.getUnidentifiedAccessKey()).thenReturn(Optional.of("1337".getBytes()));
when(accounts.get(EXISTS_NUMBER)).thenReturn(Optional.of(existsAccount));
+ when(accounts.get(EXISTS_UUID)).thenReturn(Optional.of(existsAccount));
+ when(accounts.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(EXISTS_NUMBER)))).thenReturn(Optional.of(existsAccount));
+ when(accounts.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(EXISTS_UUID)))).thenReturn(Optional.of(existsAccount));
+
when(accounts.get(NOT_EXISTS_NUMBER)).thenReturn(Optional.empty());
+ when(accounts.get(NOT_EXISTS_UUID)).thenReturn(Optional.empty());
+ when(accounts.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(NOT_EXISTS_NUMBER)))).thenReturn(Optional.empty());
+ when(accounts.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(NOT_EXISTS_UUID)))).thenReturn(Optional.empty());
when(rateLimiters.getPreKeysLimiter()).thenReturn(rateLimiter);
@@ -141,7 +154,7 @@ public class KeyControllerTest {
}
@Test
- public void validKeyStatusTestV2() throws Exception {
+ public void validKeyStatusTestByNumberV2() throws Exception {
PreKeyCount result = resources.getJerseyTest()
.target("/v2/keys")
.request()
@@ -155,7 +168,22 @@ public class KeyControllerTest {
}
@Test
- public void getSignedPreKeyV2() throws Exception {
+ public void validKeyStatusTestByUuidV2() throws Exception {
+ PreKeyCount result = resources.getJerseyTest()
+ .target("/v2/keys")
+ .request()
+ .header("Authorization",
+ AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
+ .get(PreKeyCount.class);
+
+ assertThat(result.getCount()).isEqualTo(4);
+
+ verify(keys).getCount(eq(AuthHelper.VALID_NUMBER), eq(1L));
+ }
+
+
+ @Test
+ public void getSignedPreKeyV2ByNumber() throws Exception {
SignedPreKey result = resources.getJerseyTest()
.target("/v2/keys/signed")
.request()
@@ -168,7 +196,20 @@ public class KeyControllerTest {
}
@Test
- public void putSignedPreKeyV2() throws Exception {
+ public void getSignedPreKeyV2ByUuid() throws Exception {
+ SignedPreKey result = resources.getJerseyTest()
+ .target("/v2/keys/signed")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
+ .get(SignedPreKey.class);
+
+ assertThat(result.getSignature()).isEqualTo(VALID_DEVICE_SIGNED_KEY.getSignature());
+ assertThat(result.getKeyId()).isEqualTo(VALID_DEVICE_SIGNED_KEY.getKeyId());
+ assertThat(result.getPublicKey()).isEqualTo(VALID_DEVICE_SIGNED_KEY.getPublicKey());
+ }
+
+ @Test
+ public void putSignedPreKeyV2ByNumber() throws Exception {
SignedPreKey test = new SignedPreKey(9999, "fooozzz", "baaarzzz");
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
@@ -183,7 +224,23 @@ public class KeyControllerTest {
}
@Test
- public void disabledPutSignedPreKeyV2() throws Exception {
+ public void putSignedPreKeyV2ByUuid() throws Exception {
+ SignedPreKey test = new SignedPreKey(9998, "fooozzz", "baaarzzz");
+ Response response = resources.getJerseyTest()
+ .target("/v2/keys/signed")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
+ .put(Entity.entity(test, MediaType.APPLICATION_JSON_TYPE));
+
+ assertThat(response.getStatus()).isEqualTo(204);
+
+ verify(AuthHelper.VALID_DEVICE).setSignedPreKey(eq(test));
+ verify(accounts).update(eq(AuthHelper.VALID_ACCOUNT));
+ }
+
+
+ @Test
+ public void disabledPutSignedPreKeyV2ByNumber() throws Exception {
SignedPreKey test = new SignedPreKey(9999, "fooozzz", "baaarzzz");
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
@@ -195,7 +252,20 @@ public class KeyControllerTest {
}
@Test
- public void validSingleRequestTestV2() throws Exception {
+ public void disabledPutSignedPreKeyV2ByUuid() throws Exception {
+ SignedPreKey test = new SignedPreKey(9999, "fooozzz", "baaarzzz");
+ Response response = resources.getJerseyTest()
+ .target("/v2/keys/signed")
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID.toString(), AuthHelper.DISABLED_PASSWORD))
+ .put(Entity.entity(test, MediaType.APPLICATION_JSON_TYPE));
+
+ assertThat(response.getStatus()).isEqualTo(401);
+ }
+
+
+ @Test
+ public void validSingleRequestTestV2ByNumber() throws Exception {
PreKeyResponse result = resources.getJerseyTest()
.target(String.format("/v2/keys/%s/1", EXISTS_NUMBER))
.request()
@@ -213,7 +283,26 @@ public class KeyControllerTest {
}
@Test
- public void testUnidentifiedRequest() throws Exception {
+ public void validSingleRequestTestV2ByUuid() throws Exception {
+ PreKeyResponse result = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/1", EXISTS_UUID))
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
+ .get(PreKeyResponse.class);
+
+ assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
+ 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);
+ }
+
+
+ @Test
+ public void testUnidentifiedRequestByNumber() throws Exception {
PreKeyResponse result = resources.getJerseyTest()
.target(String.format("/v2/keys/%s/1", EXISTS_NUMBER))
.request()
@@ -230,6 +319,25 @@ public class KeyControllerTest {
verifyNoMoreInteractions(keys);
}
+ @Test
+ public void testUnidentifiedRequestByUuid() throws Exception {
+ PreKeyResponse result = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/1", EXISTS_UUID.toString()))
+ .request()
+ .header(OptionalAccess.UNIDENTIFIED, AuthHelper.getUnidentifiedAccessHeader("1337".getBytes()))
+ .get(PreKeyResponse.class);
+
+ assertThat(result.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
+ 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);
+ }
+
+
@Test
public void testUnauthorizedUnidentifiedRequest() throws Exception {
Response response = resources.getJerseyTest()
@@ -256,7 +364,7 @@ public class KeyControllerTest {
@Test
- public void validMultiRequestTestV2() throws Exception {
+ public void validMultiRequestTestV2ByNumber() throws Exception {
PreKeyResponse results = resources.getJerseyTest()
.target(String.format("/v2/keys/%s/*", EXISTS_NUMBER))
.request()
@@ -305,6 +413,57 @@ public class KeyControllerTest {
verifyNoMoreInteractions(keys);
}
+ @Test
+ public void validMultiRequestTestV2ByUuid() throws Exception {
+ PreKeyResponse results = resources.getJerseyTest()
+ .target(String.format("/v2/keys/%s/*", EXISTS_UUID.toString()))
+ .request()
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
+ .get(PreKeyResponse.class);
+
+ assertThat(results.getDevicesCount()).isEqualTo(3);
+ assertThat(results.getIdentityKey()).isEqualTo(existsAccount.getIdentityKey());
+
+ PreKey signedPreKey = results.getDevice(1).getSignedPreKey();
+ PreKey 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());
+ assertThat(registrationId).isEqualTo(SAMPLE_REGISTRATION_ID);
+ assertThat(signedPreKey.getKeyId()).isEqualTo(SAMPLE_SIGNED_KEY.getKeyId());
+ assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY.getPublicKey());
+ assertThat(deviceId).isEqualTo(1);
+
+ 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());
+ assertThat(registrationId).isEqualTo(SAMPLE_REGISTRATION_ID2);
+ assertThat(signedPreKey.getKeyId()).isEqualTo(SAMPLE_SIGNED_KEY2.getKeyId());
+ assertThat(signedPreKey.getPublicKey()).isEqualTo(SAMPLE_SIGNED_KEY2.getPublicKey());
+ assertThat(deviceId).isEqualTo(2);
+
+ 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());
+ assertThat(registrationId).isEqualTo(SAMPLE_REGISTRATION_ID4);
+ assertThat(signedPreKey).isNull();
+ assertThat(deviceId).isEqualTo(4);
+
+ verify(keys).get(eq(EXISTS_NUMBER));
+ verifyNoMoreInteractions(keys);
+ }
+
+
@Test
public void invalidRequestTestV2() throws Exception {
Response response = resources.getJerseyTest()
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
index 5ac266cdf..17114d058 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/MessageControllerTest.java
@@ -7,6 +7,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.controllers.MessageController;
@@ -55,7 +57,10 @@ import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixtur
public class MessageControllerTest {
private static final String SINGLE_DEVICE_RECIPIENT = "+14151111111";
+ private static final UUID SINGLE_DEVICE_UUID = UUID.randomUUID();
+
private static final String MULTI_DEVICE_RECIPIENT = "+14152222222";
+ private static final UUID MULTI_DEVICE_UUID = UUID.randomUUID();
private final PushSender pushSender = mock(PushSender.class );
private final ReceiptSender receiptSender = mock(ReceiptSender.class);
@@ -89,11 +94,13 @@ public class MessageControllerTest {
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", true, 0));
}};
- Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, singleDeviceList, "1234".getBytes());
- Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, multiDeviceList, "1234".getBytes());
+ Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes());
+ Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, MULTI_DEVICE_UUID, multiDeviceList, "1234".getBytes());
when(accountsManager.get(eq(SINGLE_DEVICE_RECIPIENT))).thenReturn(Optional.of(singleDeviceAccount));
+ when(accountsManager.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(SINGLE_DEVICE_RECIPIENT)))).thenReturn(Optional.of(singleDeviceAccount));
when(accountsManager.get(eq(MULTI_DEVICE_RECIPIENT))).thenReturn(Optional.of(multiDeviceAccount));
+ when(accountsManager.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(MULTI_DEVICE_RECIPIENT)))).thenReturn(Optional.of(multiDeviceAccount));
when(rateLimiters.getMessagesLimiter()).thenReturn(rateLimiter);
}
@@ -240,11 +247,12 @@ public class MessageControllerTest {
final long timestampOne = 313377;
final long timestampTwo = 313388;
- final UUID uuidOne = UUID.randomUUID();
+ final UUID messageGuidOne = UUID.randomUUID();
+ final UUID sourceUuid = UUID.randomUUID();
- List messages = new LinkedList() {{
- add(new OutgoingMessageEntity(1L, false, uuidOne, Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null, 0));
- add(new OutgoingMessageEntity(2L, false, null, Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null, 0));
+ List messages = new LinkedList<>() {{
+ add(new OutgoingMessageEntity(1L, false, messageGuidOne, Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", sourceUuid, 2, "hi there".getBytes(), null, 0));
+ add(new OutgoingMessageEntity(2L, false, null, Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", sourceUuid, 2, null, null, 0));
}};
OutgoingMessageEntityList messagesList = new OutgoingMessageEntityList(messages, false);
@@ -254,7 +262,7 @@ public class MessageControllerTest {
OutgoingMessageEntityList response =
resources.getJerseyTest().target("/v1/messages/")
.request()
- .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
.accept(MediaType.APPLICATION_JSON_TYPE)
.get(OutgoingMessageEntityList.class);
@@ -267,8 +275,11 @@ public class MessageControllerTest {
assertEquals(response.getMessages().get(0).getTimestamp(), timestampOne);
assertEquals(response.getMessages().get(1).getTimestamp(), timestampTwo);
- assertEquals(response.getMessages().get(0).getGuid(), uuidOne);
- assertEquals(response.getMessages().get(1).getGuid(), null);
+ assertEquals(response.getMessages().get(0).getGuid(), messageGuidOne);
+ assertNull(response.getMessages().get(1).getGuid());
+
+ assertEquals(response.getMessages().get(0).getSourceUuid(), sourceUuid);
+ assertEquals(response.getMessages().get(1).getSourceUuid(), sourceUuid);
}
@Test
@@ -277,8 +288,8 @@ public class MessageControllerTest {
final long timestampTwo = 313388;
List messages = new LinkedList() {{
- add(new OutgoingMessageEntity(1L, false, UUID.randomUUID(), Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", 2, "hi there".getBytes(), null, 0));
- add(new OutgoingMessageEntity(2L, false, UUID.randomUUID(), Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", 2, null, null, 0));
+ add(new OutgoingMessageEntity(1L, false, UUID.randomUUID(), Envelope.Type.CIPHERTEXT_VALUE, null, timestampOne, "+14152222222", UUID.randomUUID(), 2, "hi there".getBytes(), null, 0));
+ add(new OutgoingMessageEntity(2L, false, UUID.randomUUID(), Envelope.Type.RECEIPT_VALUE, null, timestampTwo, "+14152222222", UUID.randomUUID(), 2, null, null, 0));
}};
OutgoingMessageEntityList messagesList = new OutgoingMessageEntityList(messages, false);
@@ -288,7 +299,7 @@ public class MessageControllerTest {
Response response =
resources.getJerseyTest().target("/v1/messages/")
.request()
- .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.INVALID_PASSWORD))
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.INVALID_PASSWORD))
.accept(MediaType.APPLICATION_JSON_TYPE)
.get();
@@ -299,17 +310,19 @@ public class MessageControllerTest {
public synchronized void testDeleteMessages() throws Exception {
long timestamp = System.currentTimeMillis();
+ UUID sourceUuid = UUID.randomUUID();
+
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31337))
.thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true, null,
Envelope.Type.CIPHERTEXT_VALUE,
null, timestamp,
- "+14152222222", 1, "hi".getBytes(), null, 0)));
+ "+14152222222", sourceUuid, 1, "hi".getBytes(), null, 0)));
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31338))
.thenReturn(Optional.of(new OutgoingMessageEntity(31337L, true, null,
Envelope.Type.RECEIPT_VALUE,
null, System.currentTimeMillis(),
- "+14152222222", 1, null, null, 0)));
+ "+14152222222", sourceUuid, 1, null, null, 0)));
when(messagesManager.delete(AuthHelper.VALID_NUMBER, 1, "+14152222222", 31339))
@@ -327,7 +340,7 @@ public class MessageControllerTest {
response = resources.getJerseyTest()
.target(String.format("/v1/messages/%s/%d", "+14152222222", 31338))
.request()
- .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
+ .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID.toString(), AuthHelper.VALID_PASSWORD))
.delete();
assertThat("Good Response Code", response.getStatus(), is(equalTo(204)));
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
index fab22bbd8..a03d3e164 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java
@@ -5,6 +5,8 @@ import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.controllers.ProfileController;
@@ -63,6 +65,7 @@ public class ProfileControllerTest {
when(profileAccount.isEnabled()).thenReturn(true);
when(accountsManager.get(AuthHelper.VALID_NUMBER_TWO)).thenReturn(Optional.of(profileAccount));
+ when(accountsManager.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)))).thenReturn(Optional.of(profileAccount));
}
@@ -78,7 +81,7 @@ public class ProfileControllerTest {
assertThat(profile.getName()).isEqualTo("baz");
assertThat(profile.getAvatar()).isEqualTo("profiles/bang");
- verify(accountsManager, times(1)).get(AuthHelper.VALID_NUMBER_TWO);
+ verify(accountsManager, times(1)).get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)));
verify(rateLimiters, times(1)).getProfileLimiter();
verify(rateLimiter, times(1)).validate(eq(AuthHelper.VALID_NUMBER));
}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/TransparentDataControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/TransparentDataControllerTest.java
deleted file mode 100644
index 11ea728f8..000000000
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/TransparentDataControllerTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package org.whispersystems.textsecuregcm.tests.controllers;
-
-import com.google.common.collect.ImmutableSet;
-import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
-import org.whispersystems.textsecuregcm.controllers.TransparentDataController;
-import org.whispersystems.textsecuregcm.entities.SignedPreKey;
-import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
-import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.AccountsManager;
-import org.whispersystems.textsecuregcm.storage.Device;
-import org.whispersystems.textsecuregcm.storage.PublicAccount;
-import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
-import org.whispersystems.textsecuregcm.util.SystemMapper;
-
-import javax.ws.rs.core.Response;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
-import io.dropwizard.testing.junit.ResourceTestRule;
-import static junit.framework.TestCase.*;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.*;
-import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.asJson;
-import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixture;
-
-public class TransparentDataControllerTest {
-
- private final AccountsManager accountsManager = mock(AccountsManager.class);
- private final Map indexMap = new HashMap<>();
-
- @Rule
- public final ResourceTestRule resources = ResourceTestRule.builder()
- .addProvider(AuthHelper.getAuthFilter())
- .addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class)))
- .addProvider(new RateLimitExceededExceptionMapper())
- .setMapper(SystemMapper.getMapper())
- .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
- .addResource(new TransparentDataController(accountsManager, indexMap))
- .build();
-
-
- @Before
- public void setup() {
- Account accountOne = new Account("+14151231111", Collections.singleton(new Device(1, "foo", "bar", "salt", "keykey", "gcm-id", "apn-id", "voipapn-id", true, 1234, new SignedPreKey(5, "public-signed", "signtture-signed"), 31337, 31336, "CoolClient", true, 0)), new byte[16]);
- Account accountTwo = new Account("+14151232222", Collections.singleton(new Device(1, "2foo", "2bar", "2salt", "2keykey", "2gcm-id", "2apn-id", "2voipapn-id", true, 1234, new SignedPreKey(5, "public-signed", "signtture-signed"), 31337, 31336, "CoolClient", true, 0)), new byte[16]);
-
- accountOne.setProfileName("OneProfileName");
- accountOne.setIdentityKey("identity_key_value");
- accountTwo.setProfileName("TwoProfileName");
- accountTwo.setIdentityKey("different_identity_key_value");
-
-
- indexMap.put("1", "+14151231111");
- indexMap.put("2", "+14151232222");
-
- when(accountsManager.get(eq("+14151231111"))).thenReturn(Optional.of(accountOne));
- when(accountsManager.get(eq("+14151232222"))).thenReturn(Optional.of(accountTwo));
- }
-
- @Test
- public void testAccountOne() throws IOException {
- Response response = resources.getJerseyTest()
- .target(String.format("/v1/transparency/account/%s", "1"))
- .request()
- .get();
-
- assertEquals(200, response.getStatus());
-
- Account result = response.readEntity(PublicAccount.class);
-
- assertTrue(result.getPin().isPresent());
- assertEquals("******", result.getPin().get());
- assertNull(result.getNumber());
- assertEquals("OneProfileName", result.getProfileName());
-
- assertThat("Account serialization works",
- asJson(result),
- is(equalTo(jsonFixture("fixtures/transparent_account.json"))));
-
- verify(accountsManager, times(1)).get(eq("+14151231111"));
- verifyNoMoreInteractions(accountsManager);
- }
-
- @Test
- public void testAccountTwo() throws IOException {
- Response response = resources.getJerseyTest()
- .target(String.format("/v1/transparency/account/%s", "2"))
- .request()
- .get();
-
- assertEquals(200, response.getStatus());
-
- Account result = response.readEntity(PublicAccount.class);
-
- assertTrue(result.getPin().isPresent());
- assertEquals("******", result.getPin().get());
- assertNull(result.getNumber());
- assertEquals("TwoProfileName", result.getProfileName());
-
- assertThat("Account serialization works 2",
- asJson(result),
- is(equalTo(jsonFixture("fixtures/transparent_account2.json"))));
-
- verify(accountsManager, times(1)).get(eq("+14151232222"));
- }
-
- @Test
- public void testAccountMissing() {
- Response response = resources.getJerseyTest()
- .target(String.format("/v1/transparency/account/%s", "3"))
- .request()
- .get();
-
- assertEquals(404, response.getStatus());
- verifyNoMoreInteractions(accountsManager);
- }
-
-}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountDatabaseCrawlerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountDatabaseCrawlerTest.java
index 41cb6d576..1ce7ef08a 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountDatabaseCrawlerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountDatabaseCrawlerTest.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
@@ -17,21 +17,19 @@
package org.whispersystems.textsecuregcm.tests.storage;
+import org.junit.Before;
+import org.junit.Test;
import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
-
-import org.junit.Before;
-import org.junit.Test;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -42,8 +40,8 @@ import static org.mockito.Mockito.*;
public class AccountDatabaseCrawlerTest {
- private static final String ACCOUNT1 = "+1";
- private static final String ACCOUNT2 = "+2";
+ private static final UUID ACCOUNT1 = UUID.randomUUID();
+ private static final UUID ACCOUNT2 = UUID.randomUUID();
private static final int CHUNK_SIZE = 1000;
private static final long CHUNK_INTERVAL_MS = 30_000L;
@@ -59,8 +57,8 @@ public class AccountDatabaseCrawlerTest {
@Before
public void setup() {
- when(account1.getNumber()).thenReturn(ACCOUNT1);
- when(account2.getNumber()).thenReturn(ACCOUNT2);
+ when(account1.getUuid()).thenReturn(ACCOUNT1);
+ when(account2.getUuid()).thenReturn(ACCOUNT2);
when(accounts.getAllFrom(anyInt())).thenReturn(Arrays.asList(account1, account2));
when(accounts.getAllFrom(eq(ACCOUNT1), anyInt())).thenReturn(Arrays.asList(account2));
@@ -72,20 +70,20 @@ public class AccountDatabaseCrawlerTest {
@Test
public void testCrawlStart() throws AccountDatabaseCrawlerRestartException {
- when(cache.getLastNumber()).thenReturn(Optional.empty());
+ when(cache.getLastUuid()).thenReturn(Optional.empty());
boolean accelerated = crawler.doPeriodicWork();
assertThat(accelerated).isFalse();
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
- verify(cache, times(1)).getLastNumber();
+ verify(cache, times(1)).getLastUuid();
verify(listener, times(1)).onCrawlStart();
verify(accounts, times(1)).getAllFrom(eq(CHUNK_SIZE));
- verify(accounts, times(0)).getAllFrom(any(String.class), eq(CHUNK_SIZE));
- verify(account1, times(0)).getNumber();
- verify(account2, times(1)).getNumber();
+ verify(accounts, times(0)).getAllFrom(any(UUID.class), eq(CHUNK_SIZE));
+ verify(account1, times(0)).getUuid();
+ verify(account2, times(1)).getUuid();
verify(listener, times(1)).onCrawlChunk(eq(Optional.empty()), eq(Arrays.asList(account1, account2)));
- verify(cache, times(1)).setLastNumber(eq(Optional.of(ACCOUNT2)));
+ verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
verify(cache, times(1)).isAccelerated();
verify(cache, times(1)).releaseActiveWork(any(String.class));
@@ -98,18 +96,18 @@ public class AccountDatabaseCrawlerTest {
@Test
public void testCrawlChunk() throws AccountDatabaseCrawlerRestartException {
- when(cache.getLastNumber()).thenReturn(Optional.of(ACCOUNT1));
+ when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
boolean accelerated = crawler.doPeriodicWork();
assertThat(accelerated).isFalse();
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
- verify(cache, times(1)).getLastNumber();
+ verify(cache, times(1)).getLastUuid();
verify(accounts, times(0)).getAllFrom(eq(CHUNK_SIZE));
verify(accounts, times(1)).getAllFrom(eq(ACCOUNT1), eq(CHUNK_SIZE));
- verify(account2, times(1)).getNumber();
+ verify(account2, times(1)).getUuid();
verify(listener, times(1)).onCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(Arrays.asList(account2)));
- verify(cache, times(1)).setLastNumber(eq(Optional.of(ACCOUNT2)));
+ verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
verify(cache, times(1)).isAccelerated();
verify(cache, times(1)).releaseActiveWork(any(String.class));
@@ -124,18 +122,18 @@ public class AccountDatabaseCrawlerTest {
@Test
public void testCrawlChunkAccelerated() throws AccountDatabaseCrawlerRestartException {
when(cache.isAccelerated()).thenReturn(true);
- when(cache.getLastNumber()).thenReturn(Optional.of(ACCOUNT1));
+ when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
boolean accelerated = crawler.doPeriodicWork();
assertThat(accelerated).isTrue();
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
- verify(cache, times(1)).getLastNumber();
+ verify(cache, times(1)).getLastUuid();
verify(accounts, times(0)).getAllFrom(eq(CHUNK_SIZE));
verify(accounts, times(1)).getAllFrom(eq(ACCOUNT1), eq(CHUNK_SIZE));
- verify(account2, times(1)).getNumber();
+ verify(account2, times(1)).getUuid();
verify(listener, times(1)).onCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(Arrays.asList(account2)));
- verify(cache, times(1)).setLastNumber(eq(Optional.of(ACCOUNT2)));
+ verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
verify(cache, times(1)).isAccelerated();
verify(cache, times(1)).releaseActiveWork(any(String.class));
@@ -149,19 +147,19 @@ public class AccountDatabaseCrawlerTest {
@Test
public void testCrawlChunkRestart() throws AccountDatabaseCrawlerRestartException {
- when(cache.getLastNumber()).thenReturn(Optional.of(ACCOUNT1));
+ when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
doThrow(AccountDatabaseCrawlerRestartException.class).when(listener).onCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(Arrays.asList(account2)));
boolean accelerated = crawler.doPeriodicWork();
assertThat(accelerated).isFalse();
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
- verify(cache, times(1)).getLastNumber();
+ verify(cache, times(1)).getLastUuid();
verify(accounts, times(0)).getAllFrom(eq(CHUNK_SIZE));
verify(accounts, times(1)).getAllFrom(eq(ACCOUNT1), eq(CHUNK_SIZE));
verify(account2, times(0)).getNumber();
verify(listener, times(1)).onCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(Arrays.asList(account2)));
- verify(cache, times(1)).setLastNumber(eq(Optional.empty()));
+ verify(cache, times(1)).setLastUuid(eq(Optional.empty()));
verify(cache, times(1)).clearAccelerate();
verify(cache, times(1)).isAccelerated();
verify(cache, times(1)).releaseActiveWork(any(String.class));
@@ -176,19 +174,19 @@ public class AccountDatabaseCrawlerTest {
@Test
public void testCrawlEnd() {
- when(cache.getLastNumber()).thenReturn(Optional.of(ACCOUNT2));
+ when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT2));
boolean accelerated = crawler.doPeriodicWork();
assertThat(accelerated).isFalse();
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
- verify(cache, times(1)).getLastNumber();
+ verify(cache, times(1)).getLastUuid();
verify(accounts, times(0)).getAllFrom(eq(CHUNK_SIZE));
verify(accounts, times(1)).getAllFrom(eq(ACCOUNT2), eq(CHUNK_SIZE));
verify(account1, times(0)).getNumber();
verify(account2, times(0)).getNumber();
verify(listener, times(1)).onCrawlEnd(eq(Optional.of(ACCOUNT2)));
- verify(cache, times(1)).setLastNumber(eq(Optional.empty()));
+ verify(cache, times(1)).setLastUuid(eq(Optional.empty()));
verify(cache, times(1)).clearAccelerate();
verify(cache, times(1)).isAccelerated();
verify(cache, times(1)).releaseActiveWork(any(String.class));
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
index e856f08a8..9a4e4d549 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java
@@ -7,6 +7,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
import java.util.HashSet;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
@@ -47,21 +48,21 @@ public class AccountTest {
@Test
public void testAccountActive() {
- Account recentAccount = new Account("+14152222222", new HashSet() {{
+ Account recentAccount = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{
add(recentMasterDevice);
add(recentSecondaryDevice);
}}, "1234".getBytes());
assertTrue(recentAccount.isEnabled());
- Account oldSecondaryAccount = new Account("+14152222222", new HashSet() {{
+ Account oldSecondaryAccount = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{
add(recentMasterDevice);
add(agingSecondaryDevice);
}}, "1234".getBytes());
assertTrue(oldSecondaryAccount.isEnabled());
- Account agingPrimaryAccount = new Account("+14152222222", new HashSet() {{
+ Account agingPrimaryAccount = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{
add(oldMasterDevice);
add(agingSecondaryDevice);
}}, "1234".getBytes());
@@ -71,7 +72,7 @@ public class AccountTest {
@Test
public void testAccountInactive() {
- Account oldPrimaryAccount = new Account("+14152222222", new HashSet() {{
+ Account oldPrimaryAccount = new Account("+14152222222", UUID.randomUUID(), new HashSet() {{
add(oldMasterDevice);
add(oldSecondaryDevice);
}}, "1234".getBytes());
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java
index 1d68203f6..e49389821 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsManagerTest.java
@@ -9,6 +9,7 @@ import org.whispersystems.textsecuregcm.storage.DirectoryManager;
import java.util.HashSet;
import java.util.Optional;
+import java.util.UUID;
import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertTrue;
@@ -21,14 +22,17 @@ import redis.clients.jedis.exceptions.JedisException;
public class AccountsManagerTest {
@Test
- public void testGetAccountInCache() {
+ public void testGetAccountByNumberInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Accounts accounts = mock(Accounts.class );
DirectoryManager directoryManager = mock(DirectoryManager.class );
+ UUID uuid = UUID.randomUUID();
+
when(cacheClient.getReadResource()).thenReturn(jedis);
- when(jedis.get(eq("Account5+14152222222"))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
+ when(jedis.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString());
+ when(jedis.get(eq("Account::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
Optional account = accountsManager.get("+14152222222");
@@ -37,23 +41,52 @@ public class AccountsManagerTest {
assertEquals(account.get().getNumber(), "+14152222222");
assertEquals(account.get().getProfileName(), "test");
- verify(jedis, times(1)).get(eq("Account5+14152222222"));
+ verify(jedis, times(1)).get(eq("AccountMap::+14152222222"));
+ verify(jedis, times(1)).get(eq("Account::" + uuid.toString()));
+ verify(jedis, times(2)).close();
+ verifyNoMoreInteractions(jedis);
+ verifyNoMoreInteractions(accounts);
+ }
+
+ @Test
+ public void testGetAccountByUuidInCache() {
+ ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
+ Jedis jedis = mock(Jedis.class );
+ Accounts accounts = mock(Accounts.class );
+ DirectoryManager directoryManager = mock(DirectoryManager.class );
+
+ UUID uuid = UUID.randomUUID();
+
+ when(cacheClient.getReadResource()).thenReturn(jedis);
+ when(jedis.get(eq("Account::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}");
+
+ AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
+ Optional account = accountsManager.get(uuid);
+
+ assertTrue(account.isPresent());
+ assertEquals(account.get().getNumber(), "+14152222222");
+ assertEquals(account.get().getUuid(), uuid);
+ assertEquals(account.get().getProfileName(), "test");
+
+ verify(jedis, times(1)).get(eq("Account::" + uuid.toString()));
verify(jedis, times(1)).close();
verifyNoMoreInteractions(jedis);
verifyNoMoreInteractions(accounts);
}
+
@Test
- public void testGetAccountNotInCache() {
+ public void testGetAccountByNumberNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Accounts accounts = mock(Accounts.class );
DirectoryManager directoryManager = mock(DirectoryManager.class );
- Account account = new Account("+14152222222", new HashSet<>(), new byte[16]);
+ UUID uuid = UUID.randomUUID();
+ Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
- when(jedis.get(eq("Account5+14152222222"))).thenReturn(null);
+ when(jedis.get(eq("AccountMap::+14152222222"))).thenReturn(null);
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
@@ -62,8 +95,9 @@ public class AccountsManagerTest {
assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account);
- verify(jedis, times(1)).get(eq("Account5+14152222222"));
- verify(jedis, times(1)).set(eq("Account5+14152222222"), anyString());
+ verify(jedis, times(1)).get(eq("AccountMap::+14152222222"));
+ verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
+ verify(jedis, times(1)).set(eq("Account::" + uuid.toString()), anyString());
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
@@ -72,16 +106,47 @@ public class AccountsManagerTest {
}
@Test
- public void testGetAccountBrokenCache() {
+ public void testGetAccountByUuidNotInCache() {
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
Jedis jedis = mock(Jedis.class );
Accounts accounts = mock(Accounts.class );
DirectoryManager directoryManager = mock(DirectoryManager.class );
- Account account = new Account("+14152222222", new HashSet<>(), new byte[16]);
+ UUID uuid = UUID.randomUUID();
+ Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
when(cacheClient.getReadResource()).thenReturn(jedis);
when(cacheClient.getWriteResource()).thenReturn(jedis);
- when(jedis.get(eq("Account5+14152222222"))).thenThrow(new JedisException("Connection lost!"));
+ when(jedis.get(eq("Account::" + uuid))).thenReturn(null);
+ when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
+
+ AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
+ Optional retrieved = accountsManager.get(uuid);
+
+ assertTrue(retrieved.isPresent());
+ assertSame(retrieved.get(), account);
+
+ verify(jedis, times(1)).get(eq("Account::" + uuid));
+ verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
+ verify(jedis, times(1)).set(eq("Account::" + uuid.toString()), anyString());
+ verify(jedis, times(2)).close();
+ verifyNoMoreInteractions(jedis);
+
+ verify(accounts, times(1)).get(eq(uuid));
+ verifyNoMoreInteractions(accounts);
+ }
+
+ @Test
+ public void testGetAccountByNumberBrokenCache() {
+ ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
+ Jedis jedis = mock(Jedis.class );
+ Accounts accounts = mock(Accounts.class );
+ DirectoryManager directoryManager = mock(DirectoryManager.class );
+ UUID uuid = UUID.randomUUID();
+ Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
+
+ when(cacheClient.getReadResource()).thenReturn(jedis);
+ when(cacheClient.getWriteResource()).thenReturn(jedis);
+ when(jedis.get(eq("AccountMap::+14152222222"))).thenThrow(new JedisException("Connection lost!"));
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
@@ -90,8 +155,9 @@ public class AccountsManagerTest {
assertTrue(retrieved.isPresent());
assertSame(retrieved.get(), account);
- verify(jedis, times(1)).get(eq("Account5+14152222222"));
- verify(jedis, times(1)).set(eq("Account5+14152222222"), anyString());
+ verify(jedis, times(1)).get(eq("AccountMap::+14152222222"));
+ verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
+ verify(jedis, times(1)).set(eq("Account::" + uuid.toString()), anyString());
verify(jedis, times(2)).close();
verifyNoMoreInteractions(jedis);
@@ -99,6 +165,35 @@ public class AccountsManagerTest {
verifyNoMoreInteractions(accounts);
}
+ @Test
+ public void testGetAccountByUuidBrokenCache() {
+ ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
+ Jedis jedis = mock(Jedis.class );
+ Accounts accounts = mock(Accounts.class );
+ DirectoryManager directoryManager = mock(DirectoryManager.class );
+ UUID uuid = UUID.randomUUID();
+ Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
+
+ when(cacheClient.getReadResource()).thenReturn(jedis);
+ when(cacheClient.getWriteResource()).thenReturn(jedis);
+ when(jedis.get(eq("Account::" + uuid))).thenThrow(new JedisException("Connection lost!"));
+ when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
+
+ AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheClient);
+ Optional retrieved = accountsManager.get(uuid);
+
+ assertTrue(retrieved.isPresent());
+ assertSame(retrieved.get(), account);
+
+ verify(jedis, times(1)).get(eq("Account::" + uuid));
+ verify(jedis, times(1)).set(eq("AccountMap::+14152222222"), eq(uuid.toString()));
+ verify(jedis, times(1)).set(eq("Account::" + uuid.toString()), anyString());
+ verify(jedis, times(2)).close();
+ verifyNoMoreInteractions(jedis);
+
+ verify(accounts, times(1)).get(eq(uuid));
+ verifyNoMoreInteractions(accounts);
+ }
}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java
index 24f7156d6..c8c893d28 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountsTest.java
@@ -1,5 +1,6 @@
package org.whispersystems.textsecuregcm.tests.storage;
+import com.fasterxml.uuid.UUIDComparator;
import com.opentable.db.postgres.embedded.LiquibasePreparer;
import com.opentable.db.postgres.junit.EmbeddedPostgresRules;
import com.opentable.db.postgres.junit.PreparedDbRule;
@@ -17,6 +18,8 @@ import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
import org.whispersystems.textsecuregcm.storage.mappers.AccountRowMapper;
+import org.whispersystems.textsecuregcm.util.Conversions;
+import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
import java.sql.PreparedStatement;
@@ -25,11 +28,13 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
+import java.util.UUID;
import io.github.resilience4j.circuitbreaker.CircuitBreakerOpenException;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -56,12 +61,12 @@ public class AccountsTest {
@Test
public void testStore() throws SQLException, IOException {
Device device = generateDevice (1 );
- Account account = generateAccount("+14151112222", Collections.singleton(device));
+ Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
accounts.create(account);
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM accounts WHERE number = ?");
- verifyStoredState(statement, "+14151112222", account);
+ verifyStoredState(statement, "+14151112222", account.getUuid(), account);
}
@Test
@@ -70,12 +75,12 @@ public class AccountsTest {
devices.add(generateDevice(1));
devices.add(generateDevice(2));
- Account account = generateAccount("+14151112222", devices);
+ Account account = generateAccount("+14151112222", UUID.randomUUID(), devices);
accounts.create(account);
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM accounts WHERE number = ?");
- verifyStoredState(statement, "+14151112222", account);
+ verifyStoredState(statement, "+14151112222", account.getUuid(), account);
}
@Test
@@ -84,13 +89,15 @@ public class AccountsTest {
devicesFirst.add(generateDevice(1));
devicesFirst.add(generateDevice(2));
- Account accountFirst = generateAccount("+14151112222", devicesFirst);
+ UUID uuidFirst = UUID.randomUUID();
+ Account accountFirst = generateAccount("+14151112222", uuidFirst, devicesFirst);
Set devicesSecond = new HashSet<>();
devicesSecond.add(generateDevice(1));
devicesSecond.add(generateDevice(2));
- Account accountSecond = generateAccount("+14152221111", devicesSecond);
+ UUID uuidSecond = UUID.randomUUID();
+ Account accountSecond = generateAccount("+14152221111", uuidSecond, devicesSecond);
accounts.create(accountFirst);
accounts.create(accountSecond);
@@ -101,31 +108,43 @@ public class AccountsTest {
assertThat(retrievedFirst.isPresent()).isTrue();
assertThat(retrievedSecond.isPresent()).isTrue();
- verifyStoredState("+14151112222", retrievedFirst.get(), accountFirst);
- verifyStoredState("+14152221111", retrievedSecond.get(), accountSecond);
+ verifyStoredState("+14151112222", uuidFirst, retrievedFirst.get(), accountFirst);
+ verifyStoredState("+14152221111", uuidSecond, retrievedSecond.get(), accountSecond);
+
+ retrievedFirst = accounts.get(uuidFirst);
+ retrievedSecond = accounts.get(uuidSecond);
+
+ assertThat(retrievedFirst.isPresent()).isTrue();
+ assertThat(retrievedSecond.isPresent()).isTrue();
+
+ verifyStoredState("+14151112222", uuidFirst, retrievedFirst.get(), accountFirst);
+ verifyStoredState("+14152221111", uuidSecond, retrievedSecond.get(), accountSecond);
}
@Test
public void testOverwrite() throws Exception {
Device device = generateDevice (1 );
- Account account = generateAccount("+14151112222", Collections.singleton(device));
+ UUID firstUuid = UUID.randomUUID();
+ Account account = generateAccount("+14151112222", firstUuid, Collections.singleton(device));
accounts.create(account);
PreparedStatement statement = db.getTestDatabase().getConnection().prepareStatement("SELECT * FROM accounts WHERE number = ?");
- verifyStoredState(statement, "+14151112222", account);
+ verifyStoredState(statement, "+14151112222", account.getUuid(), account);
+
+ UUID secondUuid = UUID.randomUUID();
device = generateDevice(1);
- account = generateAccount("+14151112222", Collections.singleton(device));
+ account = generateAccount("+14151112222", secondUuid, Collections.singleton(device));
accounts.create(account);
- verifyStoredState(statement, "+14151112222", account);
+ verifyStoredState(statement, "+14151112222", firstUuid, account);
}
@Test
public void testUpdate() {
Device device = generateDevice (1 );
- Account account = generateAccount("+14151112222", Collections.singleton(device));
+ Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
accounts.create(account);
@@ -136,7 +155,12 @@ public class AccountsTest {
Optional retrieved = accounts.get("+14151112222");
assertThat(retrieved.isPresent()).isTrue();
- verifyStoredState("+14151112222", retrieved.get(), account);
+ verifyStoredState("+14151112222", account.getUuid(), retrieved.get(), account);
+
+ retrieved = accounts.get(account.getUuid());
+
+ assertThat(retrieved.isPresent()).isTrue();
+ verifyStoredState("+14151112222", account.getUuid(), retrieved.get(), account);
}
@Test
@@ -144,24 +168,26 @@ public class AccountsTest {
List users = new ArrayList<>();
for (int i=1;i<=100;i++) {
- Account account = generateAccount("+1" + String.format("%03d", i));
+ Account account = generateAccount("+1" + String.format("%03d", i), UUID.randomUUID());
users.add(account);
accounts.create(account);
}
+ users.sort((account, t1) -> UUIDComparator.staticCompare(account.getUuid(), t1.getUuid()));
+
List retrieved = accounts.getAllFrom(10);
assertThat(retrieved.size()).isEqualTo(10);
for (int i=0;i retrieved = accounts.get("+14151112222");
assertThat(retrieved.isPresent()).isTrue();
- verifyStoredState("+14151112222", retrieved.get(), account);
+ verifyStoredState("+14151112222", account.getUuid(), retrieved.get(), account);
}
@Test
public void testMissing() {
Device device = generateDevice (1 );
- Account account = generateAccount("+14151112222", Collections.singleton(device));
+ Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
accounts.create(account);
Optional retrieved = accounts.get("+11111111");
assertThat(retrieved.isPresent()).isFalse();
+
+ retrieved = accounts.get(UUID.randomUUID());
+ assertThat(retrieved.isPresent()).isFalse();
}
@Test
@@ -203,7 +232,7 @@ public class AccountsTest {
configuration.setFailureRateThreshold(50);
Accounts accounts = new Accounts(new FaultTolerantDatabase("testAccountBreaker", jdbi, configuration));
- Account account = generateAccount("+14151112222");
+ Account account = generateAccount("+14151112222", UUID.randomUUID());
try {
accounts.update(account);
@@ -244,20 +273,20 @@ public class AccountsTest {
return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), null, "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt(), random.nextBoolean(), 0);
}
- private Account generateAccount(String number) {
+ private Account generateAccount(String number, UUID uuid) {
Device device = generateDevice(1);
- return generateAccount(number, Collections.singleton(device));
+ return generateAccount(number, uuid, Collections.singleton(device));
}
- private Account generateAccount(String number, Set devices) {
+ private Account generateAccount(String number, UUID uuid, Set devices) {
byte[] unidentifiedAccessKey = new byte[16];
Random random = new Random(System.currentTimeMillis());
Arrays.fill(unidentifiedAccessKey, (byte)random.nextInt(255));
- return new Account(number, devices, unidentifiedAccessKey);
+ return new Account(number, uuid, devices, unidentifiedAccessKey);
}
- private void verifyStoredState(PreparedStatement statement, String number, Account expecting)
+ private void verifyStoredState(PreparedStatement statement, String number, UUID uuid, Account expecting)
throws SQLException, IOException
{
statement.setString(1, number);
@@ -269,7 +298,7 @@ public class AccountsTest {
assertThat(data).isNotEmpty();
Account result = new AccountRowMapper().map(resultSet, null);
- verifyStoredState(number, result, expecting);
+ verifyStoredState(number, uuid, result, expecting);
} else {
throw new AssertionError("No data");
}
@@ -277,9 +306,10 @@ public class AccountsTest {
assertThat(resultSet.next()).isFalse();
}
- private void verifyStoredState(String number, Account result, Account expecting) {
+ private void verifyStoredState(String number, UUID uuid, Account result, Account expecting) {
assertThat(result.getNumber()).isEqualTo(number);
assertThat(result.getLastSeen()).isEqualTo(expecting.getLastSeen());
+ assertThat(result.getUuid()).isEqualTo(uuid);
assertThat(Arrays.equals(result.getUnidentifiedAccessKey().get(), expecting.getUnidentifiedAccessKey().get())).isTrue();
for (Device expectingDevice : expecting.getDevices()) {
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/ActiveUserCounterTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/ActiveUserCounterTest.java
index 77f7ebafc..934da0fb4 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/ActiveUserCounterTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/ActiveUserCounterTest.java
@@ -31,6 +31,7 @@ import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.Arrays;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.Optional;
@@ -46,9 +47,13 @@ import static org.mockito.Mockito.when;
public class ActiveUserCounterTest {
- private final String NUMBER_IOS = "+15551234567";
- private final String NUMBER_ANDROID = "+5511987654321";
- private final String NUMBER_NODEVICE = "+5215551234567";
+ private final UUID UUID_IOS = UUID.randomUUID();
+ private final UUID UUID_ANDROID = UUID.randomUUID();
+ private final UUID UUID_NODEVICE = UUID.randomUUID();
+
+ private final String ACCOUNT_NUMBER_IOS = "+15551234567";
+ private final String ACCOUNT_NUMBER_ANDROID = "+5511987654321";
+ private final String ACCOUNT_NUMBER_NODEVICE = "+5215551234567";
private final String TALLY_KEY = "active_user_tally";
@@ -79,14 +84,17 @@ public class ActiveUserCounterTest {
when(iosDevice.getGcmId()).thenReturn(null);
when(iosDevice.getLastSeen()).thenReturn(halfDayAgo);
- when(iosAccount.getNumber()).thenReturn(NUMBER_IOS);
+ when(iosAccount.getUuid()).thenReturn(UUID_IOS);
when(iosAccount.getMasterDevice()).thenReturn(Optional.of(iosDevice));
+ when(iosAccount.getNumber()).thenReturn(ACCOUNT_NUMBER_IOS);
- when(androidAccount.getNumber()).thenReturn(NUMBER_ANDROID);
+ when(androidAccount.getUuid()).thenReturn(UUID_ANDROID);
when(androidAccount.getMasterDevice()).thenReturn(Optional.of(androidDevice));
+ when(androidAccount.getNumber()).thenReturn(ACCOUNT_NUMBER_ANDROID);
- when(noDeviceAccount.getNumber()).thenReturn(NUMBER_NODEVICE);
+ when(noDeviceAccount.getUuid()).thenReturn(UUID_NODEVICE);
when(noDeviceAccount.getMasterDevice()).thenReturn(Optional.ofNullable(null));
+ when(noDeviceAccount.getNumber()).thenReturn(ACCOUNT_NUMBER_NODEVICE);
when(jedis.get(any(String.class))).thenReturn("{\"fromNumber\":\"+\",\"platforms\":{},\"countries\":{}}");
when(jedisPool.getWriteResource()).thenReturn(jedis);
@@ -137,7 +145,7 @@ public class ActiveUserCounterTest {
@Test
public void testCrawlChunkValidAccount() throws AccountDatabaseCrawlerRestartException {
- activeUserCounter.onCrawlChunk(Optional.of(NUMBER_IOS), Arrays.asList(iosAccount));
+ activeUserCounter.onCrawlChunk(Optional.of(UUID_IOS), Arrays.asList(iosAccount));
verify(iosAccount, times(1)).getMasterDevice();
verify(iosAccount, times(1)).getNumber();
@@ -148,7 +156,7 @@ public class ActiveUserCounterTest {
verify(jedisPool, times(1)).getWriteResource();
verify(jedis, times(1)).get(any(String.class));
- verify(jedis, times(1)).set(any(String.class), eq("{\"fromNumber\":\""+NUMBER_IOS+"\",\"platforms\":{\"ios\":[1,1,1,1,1]},\"countries\":{\"1\":[1,1,1,1,1]}}"));
+ verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS.toString()+"\",\"platforms\":{\"ios\":[1,1,1,1,1]},\"countries\":{\"1\":[1,1,1,1,1]}}"));
verify(jedis, times(1)).close();
verify(metricsFactory, times(0)).getReporters();
@@ -166,13 +174,13 @@ public class ActiveUserCounterTest {
@Test
public void testCrawlChunkNoDeviceAccount() throws AccountDatabaseCrawlerRestartException {
- activeUserCounter.onCrawlChunk(Optional.of(NUMBER_NODEVICE), Arrays.asList(noDeviceAccount));
+ activeUserCounter.onCrawlChunk(Optional.of(UUID_NODEVICE), Arrays.asList(noDeviceAccount));
verify(noDeviceAccount, times(1)).getMasterDevice();
verify(jedisPool, times(1)).getWriteResource();
verify(jedis, times(1)).get(eq(TALLY_KEY));
- verify(jedis, times(1)).set(any(String.class), eq("{\"fromNumber\":\""+NUMBER_NODEVICE+"\",\"platforms\":{},\"countries\":{}}"));
+ verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_NODEVICE+"\",\"platforms\":{},\"countries\":{}}"));
verify(jedis, times(1)).close();
verify(metricsFactory, times(0)).getReporters();
@@ -190,7 +198,7 @@ public class ActiveUserCounterTest {
@Test
public void testCrawlChunkMixedAccount() throws AccountDatabaseCrawlerRestartException {
- activeUserCounter.onCrawlChunk(Optional.of(NUMBER_IOS), Arrays.asList(iosAccount, androidAccount, noDeviceAccount));
+ activeUserCounter.onCrawlChunk(Optional.of(UUID_IOS), Arrays.asList(iosAccount, androidAccount, noDeviceAccount));
verify(iosAccount, times(1)).getMasterDevice();
verify(iosAccount, times(1)).getNumber();
@@ -208,7 +216,7 @@ public class ActiveUserCounterTest {
verify(jedisPool, times(1)).getWriteResource();
verify(jedis, times(1)).get(eq(TALLY_KEY));
- verify(jedis, times(1)).set(any(String.class), eq("{\"fromNumber\":\""+NUMBER_IOS+"\",\"platforms\":{\"android\":[0,0,0,1,1],\"ios\":[1,1,1,1,1]},\"countries\":{\"55\":[0,0,0,1,1],\"1\":[1,1,1,1,1]}}"));
+ verify(jedis, times(1)).set(any(String.class), eq("{\"fromUuid\":\""+UUID_IOS+"\",\"platforms\":{\"android\":[0,0,0,1,1],\"ios\":[1,1,1,1,1]},\"countries\":{\"55\":[0,0,0,1,1],\"1\":[1,1,1,1,1]}}"));
verify(jedis, times(1)).close();
verify(metricsFactory, times(0)).getReporters();
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
index 3c7d0dc4a..b2c10f5a6 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/DirectoryReconcilerTest.java
@@ -17,23 +17,23 @@
package org.whispersystems.textsecuregcm.tests.storage;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.entities.ClientContact;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
-import org.whispersystems.textsecuregcm.storage.DirectoryManager.BatchOperationHandle;
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
+import org.whispersystems.textsecuregcm.storage.DirectoryManager.BatchOperationHandle;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciler;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient;
import org.whispersystems.textsecuregcm.util.Util;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
import java.util.Arrays;
import java.util.Optional;
+import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -41,8 +41,10 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
public class DirectoryReconcilerTest {
- private static final String VALID_NUMBER = "valid";
- private static final String INACTIVE_NUMBER = "inactive";
+ private static final UUID VALID_UUID = UUID.randomUUID();
+ private static final String VALID_NUMBERRR = "+14152222222";
+ private static final UUID INACTIVE_UUID = UUID.randomUUID();
+ private static final String INACTIVE_NUMBERRR = "+14151111111";
private final Account activeAccount = mock(Account.class);
private final Account inactiveAccount = mock(Account.class);
@@ -56,9 +58,11 @@ public class DirectoryReconcilerTest {
@Before
public void setup() {
- when(activeAccount.getNumber()).thenReturn(VALID_NUMBER);
+ when(activeAccount.getUuid()).thenReturn(VALID_UUID);
when(activeAccount.isEnabled()).thenReturn(true);
- when(inactiveAccount.getNumber()).thenReturn(INACTIVE_NUMBER);
+ when(activeAccount.getNumber()).thenReturn(VALID_NUMBERRR);
+ when(inactiveAccount.getUuid()).thenReturn(INACTIVE_UUID);
+ when(inactiveAccount.getNumber()).thenReturn(INACTIVE_NUMBERRR);
when(inactiveAccount.isEnabled()).thenReturn(false);
when(directoryManager.startBatchOperation()).thenReturn(batchOperationHandle);
}
@@ -66,27 +70,28 @@ public class DirectoryReconcilerTest {
@Test
public void testCrawlChunkValid() throws AccountDatabaseCrawlerRestartException {
when(reconciliationClient.sendChunk(any())).thenReturn(successResponse);
- directoryReconciler.onCrawlChunk(Optional.of(VALID_NUMBER), Arrays.asList(activeAccount, inactiveAccount));
+ directoryReconciler.onCrawlChunk(Optional.of(VALID_UUID), Arrays.asList(activeAccount, inactiveAccount));
verify(activeAccount, times(2)).getNumber();
verify(activeAccount, times(2)).isEnabled();
- verify(inactiveAccount, times(2)).getNumber();
+ verify(inactiveAccount, times(1)).getUuid();
+ verify(inactiveAccount, times(1)).getNumber();
verify(inactiveAccount, times(2)).isEnabled();
ArgumentCaptor request = ArgumentCaptor.forClass(DirectoryReconciliationRequest.class);
verify(reconciliationClient, times(1)).sendChunk(request.capture());
- assertThat(request.getValue().getFromNumber()).isEqualTo(VALID_NUMBER);
- assertThat(request.getValue().getToNumber()).isEqualTo(INACTIVE_NUMBER);
- assertThat(request.getValue().getNumbers()).isEqualTo(Arrays.asList(VALID_NUMBER));
+ assertThat(request.getValue().getFromUuid()).isEqualTo(VALID_UUID);
+ assertThat(request.getValue().getToUuid()).isEqualTo(INACTIVE_UUID);
+ assertThat(request.getValue().getNumbers()).isEqualTo(Arrays.asList(VALID_NUMBERRR));
ArgumentCaptor addedContact = ArgumentCaptor.forClass(ClientContact.class);
verify(directoryManager, times(1)).startBatchOperation();
verify(directoryManager, times(1)).add(eq(batchOperationHandle), addedContact.capture());
- verify(directoryManager, times(1)).remove(eq(batchOperationHandle), eq(INACTIVE_NUMBER));
+ verify(directoryManager, times(1)).remove(eq(batchOperationHandle), eq(INACTIVE_NUMBERRR));
verify(directoryManager, times(1)).stopBatchOperation(eq(batchOperationHandle));
- assertThat(addedContact.getValue().getToken()).isEqualTo(Util.getContactToken(VALID_NUMBER));
+ assertThat(addedContact.getValue().getToken()).isEqualTo(Util.getContactToken(VALID_NUMBERRR));
verifyNoMoreInteractions(activeAccount);
verifyNoMoreInteractions(inactiveAccount);
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PublicAccountTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PublicAccountTest.java
deleted file mode 100644
index 6bd129631..000000000
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PublicAccountTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.whispersystems.textsecuregcm.tests.storage;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Test;
-import org.whispersystems.textsecuregcm.entities.SignedPreKey;
-import org.whispersystems.textsecuregcm.storage.Account;
-import org.whispersystems.textsecuregcm.storage.Device;
-import org.whispersystems.textsecuregcm.storage.PublicAccount;
-import org.whispersystems.textsecuregcm.util.SystemMapper;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Set;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-
-public class PublicAccountTest {
-
- @Test
- public void testPinSanitation() throws IOException {
- Set devices = Collections.singleton(new Device(1, "foo", "bar", "12345", null, "gcm-1234", null, null, true, 1234, new SignedPreKey(1, "public-foo", "signature-foo"), 31337, 31336, "Android4Life", true, 0));
- Account account = new Account("+14151231234", devices, new byte[16]);
- account.setPin("123456");
-
- PublicAccount publicAccount = new PublicAccount(account);
-
- String serialized = SystemMapper.getMapper().writeValueAsString(publicAccount);
- JsonNode result = SystemMapper.getMapper().readTree(serialized);
-
- assertEquals("******", result.get("pin").textValue());
- assertNull(result.get("number"));
- }
-
-
-}
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java
index 87a59d1f0..8a39a7c4a 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java
@@ -13,6 +13,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;
@@ -62,7 +63,7 @@ public class PushFeedbackProcessorTest {
@Test
public void testEmpty() {
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, directoryQueue);
- processor.onCrawlChunk(Optional.of("+14152222222"), Collections.emptyList());
+ processor.onCrawlChunk(Optional.of(UUID.randomUUID()), Collections.emptyList());
verifyZeroInteractions(accountsManager);
verifyZeroInteractions(directoryQueue);
@@ -71,7 +72,7 @@ public class PushFeedbackProcessorTest {
@Test
public void testUpdate() {
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, directoryQueue);
- processor.onCrawlChunk(Optional.of("+14153333333"), List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount));
+ processor.onCrawlChunk(Optional.of(UUID.randomUUID()), List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount));
verify(uninstalledDevice).setApnId(isNull());
verify(uninstalledDevice).setGcmId(isNull());
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
index 644dc2efb..1a994b3dd 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AuthHelper.java
@@ -1,7 +1,9 @@
package org.whispersystems.textsecuregcm.tests.util;
import com.google.common.collect.ImmutableMap;
+import org.mockito.ArgumentMatcher;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
+import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
@@ -11,26 +13,32 @@ import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.Base64;
import java.util.Optional;
+import java.util.UUID;
import io.dropwizard.auth.AuthFilter;
import io.dropwizard.auth.PolymorphicAuthDynamicFeature;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.auth.basic.BasicCredentials;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AuthHelper {
public static final String VALID_NUMBER = "+14150000000";
+ public static final UUID VALID_UUID = UUID.randomUUID();
public static final String VALID_PASSWORD = "foo";
public static final String VALID_NUMBER_TWO = "+201511111110";
+ public static final UUID VALID_UUID_TWO = UUID.randomUUID();
public static final String VALID_PASSWORD_TWO = "baz";
public static final String INVVALID_NUMBER = "+14151111111";
+ public static final UUID INVALID_UUID = UUID.randomUUID();
public static final String INVALID_PASSWORD = "bar";
public static final String DISABLED_NUMBER = "+78888888";
+ public static final UUID DISABLED_UUID = UUID.randomUUID();
public static final String DISABLED_PASSWORD = "poof";
public static final String VALID_IDENTITY = "BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo";
@@ -76,8 +84,11 @@ public class AuthHelper {
when(VALID_ACCOUNT_TWO.getEnabledDeviceCount()).thenReturn(6);
when(VALID_ACCOUNT.getNumber()).thenReturn(VALID_NUMBER);
+ when(VALID_ACCOUNT.getUuid()).thenReturn(VALID_UUID);
when(VALID_ACCOUNT_TWO.getNumber()).thenReturn(VALID_NUMBER_TWO);
+ when(VALID_ACCOUNT_TWO.getUuid()).thenReturn(VALID_UUID_TWO);
when(DISABLED_ACCOUNT.getNumber()).thenReturn(DISABLED_NUMBER);
+ when(DISABLED_ACCOUNT.getUuid()).thenReturn(DISABLED_UUID);
when(VALID_ACCOUNT.getAuthenticatedDevice()).thenReturn(Optional.of(VALID_DEVICE));
when(VALID_ACCOUNT_TWO.getAuthenticatedDevice()).thenReturn(Optional.of(VALID_DEVICE_TWO));
@@ -91,9 +102,21 @@ public class AuthHelper {
when(DISABLED_ACCOUNT.isEnabled()).thenReturn(false);
when(VALID_ACCOUNT.getIdentityKey()).thenReturn(VALID_IDENTITY);
+
when(ACCOUNTS_MANAGER.get(VALID_NUMBER)).thenReturn(Optional.of(VALID_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(VALID_UUID)).thenReturn(Optional.of(VALID_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(VALID_NUMBER)))).thenReturn(Optional.of(VALID_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(VALID_UUID)))).thenReturn(Optional.of(VALID_ACCOUNT));
+
when(ACCOUNTS_MANAGER.get(VALID_NUMBER_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
+ when(ACCOUNTS_MANAGER.get(VALID_UUID_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(VALID_NUMBER_TWO)))).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(VALID_UUID_TWO)))).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
+
when(ACCOUNTS_MANAGER.get(DISABLED_NUMBER)).thenReturn(Optional.of(DISABLED_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(DISABLED_UUID)).thenReturn(Optional.of(DISABLED_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(DISABLED_NUMBER)))).thenReturn(Optional.of(DISABLED_ACCOUNT));
+ when(ACCOUNTS_MANAGER.get(argThat((ArgumentMatcher) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(DISABLED_UUID)))).thenReturn(Optional.of(DISABLED_ACCOUNT));
AuthFilter accountAuthFilter = new BasicCredentialAuthFilter.Builder().setAuthenticator(new AccountAuthenticator(ACCOUNTS_MANAGER)).buildAuthFilter ();
AuthFilter disabledPermittedAccountAuthFilter = new BasicCredentialAuthFilter.Builder().setAuthenticator(new DisabledPermittedAccountAuthenticator(ACCOUNTS_MANAGER)).buildAuthFilter();
diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
index 9871c0f82..034712d0e 100644
--- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
+++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/websocket/WebSocketConnectionTest.java
@@ -105,10 +105,13 @@ public class WebSocketConnectionTest {
public void testOpen() throws Exception {
MessagesManager storedMessages = mock(MessagesManager.class);
+ UUID senderOneUuid = UUID.randomUUID();
+ UUID senderTwoUuid = UUID.randomUUID();
+
List outgoingMessages = new LinkedList () {{
- add(createMessage(1L, false, "sender1", 1111, false, "first"));
- add(createMessage(2L, false, "sender1", 2222, false, "second"));
- add(createMessage(3L, false, "sender2", 3333, false, "third"));
+ add(createMessage(1L, false, "sender1", senderOneUuid, 1111, false, "first"));
+ add(createMessage(2L, false, "sender1", senderOneUuid, 2222, false, "second"));
+ add(createMessage(3L, false, "sender2", senderTwoUuid, 3333, false, "third"));
}};
OutgoingMessageEntityList outgoingMessagesList = new OutgoingMessageEntityList(outgoingMessages, false);
@@ -121,7 +124,7 @@ public class WebSocketConnectionTest {
final Device sender1device = mock(Device.class);
- Set sender1devices = new HashSet() {{
+ Set sender1devices = new HashSet<>() {{
add(sender1device);
}};
@@ -275,6 +278,7 @@ public class WebSocketConnectionTest {
final Envelope firstMessage = Envelope.newBuilder()
.setLegacyMessage(ByteString.copyFrom("first".getBytes()))
.setSource("sender1")
+ .setSourceUuid(UUID.randomUUID().toString())
.setTimestamp(System.currentTimeMillis())
.setSourceDevice(1)
.setType(Envelope.Type.CIPHERTEXT)
@@ -283,6 +287,7 @@ public class WebSocketConnectionTest {
final Envelope secondMessage = Envelope.newBuilder()
.setLegacyMessage(ByteString.copyFrom("second".getBytes()))
.setSource("sender2")
+ .setSourceUuid(UUID.randomUUID().toString())
.setTimestamp(System.currentTimeMillis())
.setSourceDevice(2)
.setType(Envelope.Type.CIPHERTEXT)
@@ -290,11 +295,11 @@ public class WebSocketConnectionTest {
List pendingMessages = new LinkedList() {{
add(new OutgoingMessageEntity(1, true, UUID.randomUUID(), firstMessage.getType().getNumber(), firstMessage.getRelay(),
- firstMessage.getTimestamp(), firstMessage.getSource(),
+ firstMessage.getTimestamp(), firstMessage.getSource(), UUID.fromString(firstMessage.getSourceUuid()),
firstMessage.getSourceDevice(), firstMessage.getLegacyMessage().toByteArray(),
firstMessage.getContent().toByteArray(), 0));
add(new OutgoingMessageEntity(2, false, UUID.randomUUID(), secondMessage.getType().getNumber(), secondMessage.getRelay(),
- secondMessage.getTimestamp(), secondMessage.getSource(),
+ secondMessage.getTimestamp(), secondMessage.getSource(), UUID.fromString(secondMessage.getSourceUuid()),
secondMessage.getSourceDevice(), secondMessage.getLegacyMessage().toByteArray(),
secondMessage.getContent().toByteArray(), 0));
}};
@@ -359,9 +364,9 @@ public class WebSocketConnectionTest {
}
- private OutgoingMessageEntity createMessage(long id, boolean cached, String sender, long timestamp, boolean receipt, String content) {
+ private OutgoingMessageEntity createMessage(long id, boolean cached, String sender, UUID senderUuid, long timestamp, boolean receipt, String content) {
return new OutgoingMessageEntity(id, cached, UUID.randomUUID(), receipt ? Envelope.Type.RECEIPT_VALUE : Envelope.Type.CIPHERTEXT_VALUE,
- null, timestamp, sender, 1, content.getBytes(), null, 0);
+ null, timestamp, sender, senderUuid, 1, content.getBytes(), null, 0);
}
}