diff --git a/service/config/sample.yml b/service/config/sample.yml index 7ed2002f1..ddbc2bfc6 100644 --- a/service/config/sample.yml +++ b/service/config/sample.yml @@ -50,9 +50,6 @@ cacheCluster: # Redis server configuration for cache cluster - redis://redis.example.com:6379/ directory: - redis: # Redis server configuration for directory cluster - url: - replicaUrls: client: # Configuration for interfacing with Contact Discovery Service cluster userAuthenticationTokenSharedSecret: # hex-encoded secret shared with CDS used to generate auth tokens for Signal users userAuthenticationTokenUserIdSecret: # hex-encoded secret shared among Signal-Servers to obscure user phone numbers from CDS diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 675a02ed5..61b98c25e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -93,7 +93,6 @@ import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; import org.whispersystems.textsecuregcm.metrics.TrafficSource; import org.whispersystems.textsecuregcm.providers.RedisClientFactory; import org.whispersystems.textsecuregcm.providers.RedisClusterHealthCheck; -import org.whispersystems.textsecuregcm.providers.RedisHealthCheck; import org.whispersystems.textsecuregcm.push.APNSender; import org.whispersystems.textsecuregcm.push.ApnFallbackManager; import org.whispersystems.textsecuregcm.push.ClientPresenceManager; @@ -119,7 +118,6 @@ import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener; import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.ActiveUserCounter; -import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.storage.DirectoryReconciler; import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; @@ -301,11 +299,9 @@ public class WhisperServerService extends Application server; - public RedisConfiguration getRedisConfiguration() { - return redis; - } - public SqsConfiguration getSqsConfiguration() { return sqs; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryServerConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryServerConfiguration.java index c3d3bb0c8..c7154a6df 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryServerConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/DirectoryServerConfiguration.java @@ -13,9 +13,6 @@ public class DirectoryServerConfiguration { @JsonProperty private String replicationName; - @JsonProperty - private boolean replicationPrimary; - @NotEmpty @JsonProperty private String replicationUrl; @@ -32,10 +29,6 @@ public class DirectoryServerConfiguration { return replicationName; } - public boolean isReplicationPrimary() { - return replicationPrimary; - } - public String getReplicationUrl() { return replicationUrl; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/RateLimitsConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/RateLimitsConfiguration.java index 7dc94831a..477d4c9a0 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/RateLimitsConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/RateLimitsConfiguration.java @@ -35,12 +35,6 @@ public class RateLimitsConfiguration { @JsonProperty private RateLimitConfiguration attachments = new RateLimitConfiguration(50, 50); - @JsonProperty - private RateLimitConfiguration contactQueries = new RateLimitConfiguration(50000, 50000); - - @JsonProperty - private RateLimitConfiguration contactIpQueries = new RateLimitConfiguration(200, (100.0 / 60.0)); - @JsonProperty private RateLimitConfiguration prekeys = new RateLimitConfiguration(3, 1.0 / 10.0); @@ -88,14 +82,6 @@ public class RateLimitsConfiguration { return prekeys; } - public RateLimitConfiguration getContactQueries() { - return contactQueries; - } - - public RateLimitConfiguration getContactIpQueries() { - return contactIpQueries; - } - public RateLimitConfiguration getAttachments() { return attachments; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java index 22b1feb05..638da4968 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DirectoryController.java @@ -4,92 +4,28 @@ */ package org.whispersystems.textsecuregcm.controllers; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.annotation.Timed; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.dropwizard.auth.Auth; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; -import org.whispersystems.textsecuregcm.entities.ClientContact; -import org.whispersystems.textsecuregcm.entities.ClientContactTokens; -import org.whispersystems.textsecuregcm.entities.ClientContacts; -import org.whispersystems.textsecuregcm.entities.DirectoryFeedbackRequest; -import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.storage.Account; -import org.whispersystems.textsecuregcm.storage.Device; -import org.whispersystems.textsecuregcm.storage.DirectoryManager; -import org.whispersystems.textsecuregcm.util.Base64; -import org.whispersystems.textsecuregcm.util.Constants; -import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.PUT; import javax.ws.rs.Path; -import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -import static com.codahale.metrics.MetricRegistry.name; -import io.dropwizard.auth.Auth; @Path("/v1/directory") public class DirectoryController { - private static final String[] FEEDBACK_STATUSES = { - "ok", - "mismatch", - "attestation-error", - "unexpected-error", - }; - - private static final String[] FRONTED_REGIONS = {"+20", "+971", "+968", "+974"}; - - private final Logger logger = LoggerFactory.getLogger(DirectoryController.class); - private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); - private final Histogram contactsHistogram = metricRegistry.histogram(name(getClass(), "contacts")); - private final Meter contactsMeter = metricRegistry.meter(name(getClass(), "contactRate")); - - private final Map iosFeedbackMeters = new HashMap() {{ - for (String status : FEEDBACK_STATUSES) { - put(status, metricRegistry.meter(name(DirectoryController.class, "feedback-v2", "ios", status))); - } - }}; - private final Map androidFeedbackMeters = new HashMap() {{ - for (String status : FEEDBACK_STATUSES) { - put(status, metricRegistry.meter(name(DirectoryController.class, "feedback-v2", "android", status))); - } - }}; - private final Map unknownFeedbackMeters = new HashMap() {{ - for (String status : FEEDBACK_STATUSES) { - put(status, metricRegistry.meter(name(DirectoryController.class, "feedback-v2", "unknown", status))); - } - }}; - - private final RateLimiters rateLimiters; - private final DirectoryManager directory; private final ExternalServiceCredentialGenerator directoryServiceTokenGenerator; - public DirectoryController(RateLimiters rateLimiters, - DirectoryManager directory, - ExternalServiceCredentialGenerator userTokenGenerator) - { - this.directory = directory; - this.rateLimiters = rateLimiters; + public DirectoryController(ExternalServiceCredentialGenerator userTokenGenerator) { this.directoryServiceTokenGenerator = userTokenGenerator; } @@ -105,29 +41,8 @@ public class DirectoryController { @Path("/feedback-v3/{status}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public Response setFeedback(@Auth Account account, - @PathParam("status") String status, - @Valid DirectoryFeedbackRequest request) - { - Map platformFeedbackMeters = unknownFeedbackMeters; - - Optional masterDevice = account.getMasterDevice(); - if (masterDevice.isPresent()) { - if (masterDevice.get().getApnId() != null) { - platformFeedbackMeters = iosFeedbackMeters; - } else if (masterDevice.get().getGcmId() != null) { - platformFeedbackMeters = androidFeedbackMeters; - } - } - - Optional meter = Optional.ofNullable(platformFeedbackMeters.get(status)); - if (meter.isPresent()) { - meter.get().mark(); - - return Response.ok().build(); - } else { - return Response.status(Status.NOT_FOUND).build(); - } + public Response setFeedback(@Auth Account account) { + return Response.ok().build(); } @@ -135,9 +50,7 @@ public class DirectoryController { @GET @Path("/{token}") @Produces(MediaType.APPLICATION_JSON) - public Response getTokenPresence(@Auth Account account, @PathParam("token") String token) - throws RateLimitExceededException - { + public Response getTokenPresence(@Auth Account account) { return Response.status(429).build(); } @@ -146,15 +59,7 @@ public class DirectoryController { @Path("/tokens") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Response getContactIntersection(@Auth Account account, - @HeaderParam("X-Forwarded-For") String forwardedFor, - @Valid ClientContactTokens contacts) - throws RateLimitExceededException - { + public Response getContactIntersection(@Auth Account account) { return Response.status(429).build(); } - - private byte[] decodeToken(String encoded) throws IOException { - return Base64.decodeWithoutPadding(encoded.replace('-', '+').replace('_', '/')); - } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContact.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContact.java deleted file mode 100644 index 136f4c35b..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContact.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; - -import java.util.Arrays; - -@JsonInclude(JsonInclude.Include.NON_DEFAULT) -public class ClientContact { - - @JsonSerialize(using = ByteArrayAdapter.Serializing.class) - @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) - @JsonProperty - private byte[] token; - - @JsonProperty - private boolean voice; - - @JsonProperty - private boolean video; - - private String relay; - private boolean inactive; - - public ClientContact(byte[] token, String relay, boolean voice, boolean video) { - this.token = token; - this.relay = relay; - this.voice = voice; - this.video = video; - } - - public ClientContact() {} - - public byte[] getToken() { - return token; - } - - public String getRelay() { - return relay; - } - - public void setRelay(String relay) { - this.relay = relay; - } - - public boolean isInactive() { - return inactive; - } - - public void setInactive(boolean inactive) { - this.inactive = inactive; - } - - public boolean isVoice() { - return voice; - } - - public void setVoice(boolean voice) { - this.voice = voice; - } - - public boolean isVideo() { - return video; - } - - public void setVideo(boolean video) { - this.video = video; - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (!(other instanceof ClientContact)) return false; - - ClientContact that = (ClientContact)other; - - return - Arrays.equals(this.token, that.token) && - this.inactive == that.inactive && - this.voice == that.voice && - this.video == that.video && - (this.relay == null ? (that.relay == null) : this.relay.equals(that.relay)); - } - - public int hashCode() { - return Arrays.hashCode(this.token); - } - -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContactTokens.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContactTokens.java deleted file mode 100644 index 19ad7d6d9..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContactTokens.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.validation.constraints.NotNull; -import java.util.List; - -public class ClientContactTokens { - - @NotNull - @JsonProperty - private List contacts; - - public List getContacts() { - return contacts; - } - - public ClientContactTokens() {} - - public ClientContactTokens(List contacts) { - this.contacts = contacts; - } - -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContacts.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContacts.java deleted file mode 100644 index 3c1eb33b9..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ClientContacts.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.LinkedList; -import java.util.List; - -public class ClientContacts { - - @JsonProperty - private List contacts; - - public ClientContacts(List contacts) { - if (contacts != null) this.contacts = contacts; - else this.contacts = new LinkedList<>(); - } - - public ClientContacts() { - this.contacts = new LinkedList<>(); - } - - public List getContacts() { - return contacts; - } -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/DirectoryFeedbackRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/DirectoryFeedbackRequest.java deleted file mode 100644 index dc0958a81..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/DirectoryFeedbackRequest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.validation.constraints.Size; -import javax.validation.valueextraction.Unwrapping; -import java.util.Optional; - -public class DirectoryFeedbackRequest { - - @Size(max = 1024, payload = {Unwrapping.Unwrap.class}) - @JsonProperty - private Optional reason; - - public DirectoryFeedbackRequest() { - } - - public DirectoryFeedbackRequest(Optional reason) { - this.reason = reason; - } - - public Optional getReason() { - return reason; - } - -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java index df46629ed..d64cf0e4d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java @@ -25,8 +25,6 @@ public class RateLimiters { private final RateLimiter pinLimiter; private final RateLimiter attachmentLimiter; - private final RateLimiter contactsLimiter; - private final RateLimiter contactsIpLimiter; private final RateLimiter preKeysLimiter; private final RateLimiter messagesLimiter; @@ -86,14 +84,6 @@ public class RateLimiters { config.getAttachments().getBucketSize(), config.getAttachments().getLeakRatePerMinute()); - this.contactsLimiter = new RateLimiter(cacheCluster, "contactsQuery", - config.getContactQueries().getBucketSize(), - config.getContactQueries().getLeakRatePerMinute()); - - this.contactsIpLimiter = new RateLimiter(cacheCluster, "contactsIpQuery", - config.getContactIpQueries().getBucketSize(), - config.getContactIpQueries().getLeakRatePerMinute()); - this.preKeysLimiter = new RateLimiter(cacheCluster, "prekeys", config.getPreKeys().getBucketSize(), config.getPreKeys().getLeakRatePerMinute()); @@ -174,14 +164,6 @@ public class RateLimiters { return preKeysLimiter; } - public RateLimiter getContactsLimiter() { - return contactsLimiter; - } - - public RateLimiter getContactsIpLimiter() { - return contactsIpLimiter; - } - public RateLimiter getAttachmentLimiter() { return this.attachmentLimiter; } 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 eb4aeb1c6..4203451aa 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -16,7 +16,6 @@ import io.micrometer.core.instrument.Metrics; 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.FaultTolerantRedisCluster; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.util.Constants; @@ -53,7 +52,6 @@ public class AccountsManager { private final Accounts accounts; private final FaultTolerantRedisCluster cacheCluster; - private final DirectoryManager directory; private final DirectoryQueue directoryQueue; private final KeysDynamoDb keysDynamoDb; private final MessagesManager messagesManager; @@ -73,9 +71,8 @@ public class AccountsManager { } } - public AccountsManager(Accounts accounts, DirectoryManager directory, FaultTolerantRedisCluster cacheCluster, final DirectoryQueue directoryQueue, final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager, final UsernamesManager usernamesManager, final ProfilesManager profilesManager) { + public AccountsManager(Accounts accounts, FaultTolerantRedisCluster cacheCluster, final DirectoryQueue directoryQueue, final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager, final UsernamesManager usernamesManager, final ProfilesManager profilesManager) { this.accounts = accounts; - this.directory = directory; this.cacheCluster = cacheCluster; this.directoryQueue = directoryQueue; this.keysDynamoDb = keysDynamoDb; @@ -89,7 +86,6 @@ public class AccountsManager { try (Timer.Context ignored = createTimer.time()) { boolean freshUser = databaseCreate(account); redisSet(account); - updateDirectory(account); return freshUser; } @@ -99,7 +95,6 @@ public class AccountsManager { try (Timer.Context ignored = updateTimer.time()) { redisSet(account); databaseUpdate(account); - updateDirectory(account); } } @@ -148,7 +143,6 @@ public class AccountsManager { try (final Timer.Context ignored = deleteTimer.time()) { usernamesManager.delete(account.getUuid()); directoryQueue.deleteAccount(account); - directory.remove(account.getNumber()); profilesManager.deleteAll(account.getUuid()); keysDynamoDb.delete(account); messagesManager.clear(account.getNumber(), account.getUuid()); @@ -162,16 +156,6 @@ public class AccountsManager { .increment(); } - private void updateDirectory(Account account) { - if (account.isEnabled()) { - byte[] token = Util.getContactToken(account.getNumber()); - ClientContact clientContact = new ClientContact(token, null, true, true); - directory.add(clientContact); - } else { - directory.remove(account.getNumber()); - } - } - private String getAccountMapKey(String number) { return "AccountMap::" + number; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java deleted file mode 100644 index f9893dbc2..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryManager.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.storage; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.whispersystems.textsecuregcm.entities.ClientContact; -import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; -import org.whispersystems.textsecuregcm.util.IterablePair; -import org.whispersystems.textsecuregcm.util.Pair; -import org.whispersystems.textsecuregcm.util.Util; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -import redis.clients.jedis.Jedis; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.Response; - -public class DirectoryManager { - - private final Logger logger = LoggerFactory.getLogger(DirectoryManager.class); - - private static final byte[] DIRECTORY_KEY = {'d', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y'}; - - private final ObjectMapper objectMapper; - private final ReplicatedJedisPool redisPool; - - public DirectoryManager(ReplicatedJedisPool redisPool) { - this.redisPool = redisPool; - this.objectMapper = new ObjectMapper(); - this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - public void remove(String number) { - remove(Util.getContactToken(number)); - } - - public void remove(BatchOperationHandle handle, String number) { - remove(handle, Util.getContactToken(number)); - } - - public void remove(byte[] token) { - try (Jedis jedis = redisPool.getWriteResource()) { - jedis.hdel(DIRECTORY_KEY, token); - } - } - - public void remove(BatchOperationHandle handle, byte[] token) { - Pipeline pipeline = handle.pipeline; - pipeline.hdel(DIRECTORY_KEY, token); - } - - public void add(ClientContact contact) { - TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice(), contact.isVideo()); - - try (Jedis jedis = redisPool.getWriteResource()) { - jedis.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue)); - } catch (JsonProcessingException e) { - logger.warn("JSON Serialization", e); - } - } - - public void add(BatchOperationHandle handle, ClientContact contact) { - try { - Pipeline pipeline = handle.pipeline; - TokenValue tokenValue = new TokenValue(contact.getRelay(), contact.isVoice(), contact.isVideo()); - - pipeline.hset(DIRECTORY_KEY, contact.getToken(), objectMapper.writeValueAsBytes(tokenValue)); - } catch (JsonProcessingException e) { - logger.warn("JSON Serialization", e); - } - } - - public PendingClientContact get(BatchOperationHandle handle, byte[] token) { - Pipeline pipeline = handle.pipeline; - return new PendingClientContact(objectMapper, token, pipeline.hget(DIRECTORY_KEY, token)); - } - - public Optional get(byte[] token) { - try (Jedis jedis = redisPool.getWriteResource()) { - byte[] result = jedis.hget(DIRECTORY_KEY, token); - - if (result == null) { - return Optional.empty(); - } - - TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class); - return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice, tokenValue.video)); - } catch (IOException e) { - logger.warn("JSON Error", e); - return Optional.empty(); - } - } - - public List get(List tokens) { - try (Jedis jedis = redisPool.getWriteResource()) { - Pipeline pipeline = jedis.pipelined(); - List> futures = new LinkedList<>(); - List results = new LinkedList<>(); - - try { - for (byte[] token : tokens) { - futures.add(pipeline.hget(DIRECTORY_KEY, token)); - } - } finally { - pipeline.sync(); - } - - IterablePair> lists = new IterablePair<>(tokens, futures); - - for (Pair> pair : lists) { - try { - if (pair.second().get() != null) { - TokenValue tokenValue = objectMapper.readValue(pair.second().get(), TokenValue.class); - ClientContact clientContact = new ClientContact(pair.first(), tokenValue.relay, tokenValue.voice, tokenValue.video); - - results.add(clientContact); - } - } catch (IOException e) { - logger.warn("Deserialization Problem: ", e); - } - } - - return results; - } - } - - public BatchOperationHandle startBatchOperation() { - Jedis jedis = redisPool.getWriteResource(); - return new BatchOperationHandle(jedis, jedis.pipelined()); - } - - public void stopBatchOperation(BatchOperationHandle handle) { - Pipeline pipeline = handle.pipeline; - Jedis jedis = handle.jedis; - - pipeline.sync(); - jedis.close(); - } - - public static class BatchOperationHandle { - - public final Pipeline pipeline; - public final Jedis jedis; - - public BatchOperationHandle(Jedis jedis, Pipeline pipeline) { - this.pipeline = pipeline; - this.jedis = jedis; - } - } - - private static class TokenValue { - - @JsonProperty(value = "r") - private String relay; - - @JsonProperty(value = "v") - private boolean voice; - - @JsonProperty(value = "w") - private boolean video; - - public TokenValue() {} - - public TokenValue(String relay, boolean voice, boolean video) { - this.relay = relay; - this.voice = voice; - this.video = video; - } - } - - public static class PendingClientContact { - private final ObjectMapper objectMapper; - private final byte[] token; - private final Response response; - - PendingClientContact(ObjectMapper objectMapper, byte[] token, Response response) { - this.objectMapper = objectMapper; - this.token = token; - this.response = response; - } - - public Optional get() throws IOException { - byte[] result = response.get(); - - if (result == null) { - return Optional.empty(); - } - - TokenValue tokenValue = objectMapper.readValue(result, TokenValue.class); - return Optional.of(new ClientContact(token, tokenValue.relay, tokenValue.voice, tokenValue.video)); - } - - } -} 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 78c796f5c..cc99f6f28 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/DirectoryReconciler.java @@ -10,15 +10,11 @@ import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.textsecuregcm.entities.ClientContact; import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest; import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse; -import org.whispersystems.textsecuregcm.storage.DirectoryManager.BatchOperationHandle; import org.whispersystems.textsecuregcm.util.Constants; -import org.whispersystems.textsecuregcm.util.Util; import javax.ws.rs.ProcessingException; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,17 +28,11 @@ public class DirectoryReconciler extends AccountDatabaseCrawlerListener { private static final Logger logger = LoggerFactory.getLogger(DirectoryReconciler.class); private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); - private final String name; - private final boolean primary; - private final DirectoryManager directoryManager; private final DirectoryReconciliationClient reconciliationClient; private final Timer sendChunkTimer; private final Meter sendChunkErrorMeter; - public DirectoryReconciler(String name, boolean primary, DirectoryReconciliationClient reconciliationClient, DirectoryManager directoryManager) { - this.name = name; - this.primary = primary; - this.directoryManager = directoryManager; + public DirectoryReconciler(String name, DirectoryReconciliationClient reconciliationClient) { this.reconciliationClient = reconciliationClient; sendChunkTimer = metricRegistry.timer(name(DirectoryReconciler.class, name, "sendChunk")); sendChunkErrorMeter = metricRegistry.meter(name(DirectoryReconciler.class, name, "sendChunkError")); @@ -59,11 +49,6 @@ public class DirectoryReconciler extends AccountDatabaseCrawlerListener { @Override protected void onCrawlChunk(Optional fromUuid, List chunkAccounts) throws AccountDatabaseCrawlerRestartException { - - if (primary) { - updateDirectoryCache(chunkAccounts); - } - DirectoryReconciliationRequest request = createChunkRequest(fromUuid, chunkAccounts); DirectoryReconciliationResponse response = sendChunk(request); if (response.getStatus() == DirectoryReconciliationResponse.Status.MISSING) { @@ -71,25 +56,6 @@ public class DirectoryReconciler extends AccountDatabaseCrawlerListener { } } - private void updateDirectoryCache(List accounts) { - - BatchOperationHandle batchOperation = directoryManager.startBatchOperation(); - - try { - for (Account account : accounts) { - if (account.isEnabled() && account.isDiscoverableByPhoneNumber()) { - byte[] token = Util.getContactToken(account.getNumber()); - ClientContact clientContact = new ClientContact(token, null, true, true); - directoryManager.add(batchOperation, clientContact); - } else { - directoryManager.remove(batchOperation, account.getNumber()); - } - } - } finally { - directoryManager.stopBatchOperation(batchOperation); - } - } - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private DirectoryReconciliationRequest createChunkRequest(Optional fromUuid, List accounts) { List users = new ArrayList<>(accounts.size()); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java index 9022d0629..544e55cdf 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/DeleteUserCommand.java @@ -23,14 +23,11 @@ import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; import org.whispersystems.textsecuregcm.metrics.PushLatencyManager; -import org.whispersystems.textsecuregcm.providers.RedisClientFactory; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; -import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.AccountsManager; -import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase; import org.whispersystems.textsecuregcm.storage.KeysDynamoDb; @@ -121,18 +118,16 @@ public class DeleteUserCommand extends EnvironmentCommand account = accountsManager.get(user); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java index d049e8e1a..1e1650031 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DirectoryControllerTest.java @@ -6,75 +6,45 @@ package org.whispersystems.textsecuregcm.tests.controllers; import com.google.common.collect.ImmutableSet; +import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; +import io.dropwizard.testing.junit.ResourceTestRule; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; -import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount; +import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator; +import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.controllers.DirectoryController; -import org.whispersystems.textsecuregcm.entities.ClientContactTokens; -import org.whispersystems.textsecuregcm.entities.DirectoryFeedbackRequest; -import org.whispersystems.textsecuregcm.limits.RateLimiter; -import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.storage.Account; -import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; -import org.whispersystems.textsecuregcm.util.Base64; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status.Family; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; +import java.util.Collections; -import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; -import io.dropwizard.testing.junit.ResourceTestRule; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyListOf; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class DirectoryControllerTest { - private final RateLimiters rateLimiters = mock(RateLimiters.class ); - private final RateLimiter rateLimiter = mock(RateLimiter.class ); - private final RateLimiter ipLimiter = mock(RateLimiter.class ); - private final DirectoryManager directoryManager = mock(DirectoryManager.class ); private final ExternalServiceCredentialGenerator directoryCredentialsGenerator = mock(ExternalServiceCredentialGenerator.class); - - private final ExternalServiceCredentials validCredentials = new ExternalServiceCredentials("username", "password"); + private final ExternalServiceCredentials validCredentials = new ExternalServiceCredentials("username", "password"); @Rule public final ResourceTestRule resources = ResourceTestRule.builder() .addProvider(AuthHelper.getAuthFilter()) .addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class))) .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) - .addResource(new DirectoryController(rateLimiters, - directoryManager, - directoryCredentialsGenerator)) + .addResource(new DirectoryController(directoryCredentialsGenerator)) .build(); - @Before - public void setup() throws Exception { - when(rateLimiters.getContactsLimiter()).thenReturn(rateLimiter); - when(rateLimiters.getContactsIpLimiter()).thenReturn(ipLimiter); - when(directoryManager.get(anyListOf(byte[].class))).thenAnswer(new Answer>() { - @Override - public List answer(InvocationOnMock invocationOnMock) throws Throwable { - List query = (List) invocationOnMock.getArguments()[0]; - List response = new LinkedList<>(query); - response.remove(0); - return response; - } - }); + public void setup() { when(directoryCredentialsGenerator.generateFor(eq(AuthHelper.VALID_NUMBER))).thenReturn(validCredentials); } @@ -85,65 +55,10 @@ public class DirectoryControllerTest { .target("/v1/directory/feedback-v3/ok") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new DirectoryFeedbackRequest(Optional.of("test reason")), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.json("{\"reason\": \"test reason\"}")); assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL); } - @Test - public void testNotFoundFeedback() { - Response response = - resources.getJerseyTest() - .target("/v1/directory/feedback-v3/test-not-found") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new DirectoryFeedbackRequest(Optional.of("test reason")), MediaType.APPLICATION_JSON_TYPE)); - assertThat(response.getStatusInfo()).isEqualTo(Status.NOT_FOUND); - } - - @Test - public void testFeedbackEmptyRequest() { - Response response = - resources.getJerseyTest() - .target("/v1/directory/feedback-v3/mismatch") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.json("")); - assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL); - } - - @Test - public void testFeedbackNoReason() { - Response response = - resources.getJerseyTest() - .target("/v1/directory/feedback-v3/mismatch") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new DirectoryFeedbackRequest(Optional.empty()), MediaType.APPLICATION_JSON_TYPE)); - assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL); - } - - @Test - public void testFeedbackEmptyReason() { - Response response = - resources.getJerseyTest() - .target("/v1/directory/feedback-v3/mismatch") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new DirectoryFeedbackRequest(Optional.of("")), MediaType.APPLICATION_JSON_TYPE)); - assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL); - } - - @Test - public void testFeedbackTooLargeReason() { - Response response = - resources.getJerseyTest() - .target("/v1/directory/feedback-v3/mismatch") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) - .put(Entity.entity(new DirectoryFeedbackRequest(Optional.of(new String(new char[102400]))), MediaType.APPLICATION_JSON_TYPE)); - assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.CLIENT_ERROR); - } - @Test public void testGetAuthToken() { ExternalServiceCredentials token = @@ -169,16 +84,7 @@ public class DirectoryControllerTest { @Test - public void testContactIntersection() throws Exception { - List tokens = new LinkedList() {{ - add(Base64.encodeBytes("foo".getBytes())); - add(Base64.encodeBytes("bar".getBytes())); - add(Base64.encodeBytes("baz".getBytes())); - }}; - - List expectedResponse = new LinkedList<>(tokens); - expectedResponse.remove(0); - + public void testContactIntersection() { Response response = resources.getJerseyTest() .target("/v1/directory/tokens/") @@ -187,7 +93,7 @@ public class DirectoryControllerTest { AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) .header("X-Forwarded-For", "192.168.1.1, 1.1.1.1") - .put(Entity.entity(new ClientContactTokens(tokens), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(Collections.emptyMap(), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(429); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/ClientContactTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/ClientContactTest.java deleted file mode 100644 index 58b132443..000000000 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/ClientContactTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.tests.entities; - -import org.junit.Test; -import org.whispersystems.textsecuregcm.entities.ClientContact; -import org.whispersystems.textsecuregcm.util.Util; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.*; - -public class ClientContactTest { - - @Test - public void serializeToJSON() throws Exception { - byte[] token = Util.getContactToken("+14152222222"); - ClientContact contact = new ClientContact(token, null, false, false); - ClientContact contactWithRelay = new ClientContact(token, "whisper", false, false); - ClientContact contactWithRelayVox = new ClientContact(token, "whisper", true, false); - ClientContact contactWithRelayVid = new ClientContact(token, "whisper", true, true); - - assertThat("Basic Contact Serialization works", - asJson(contact), - is(equalTo(jsonFixture("fixtures/contact.json")))); - - assertThat("Contact Relay Serialization works", - asJson(contactWithRelay), - is(equalTo(jsonFixture("fixtures/contact.relay.json")))); - - assertThat("Contact Relay Vox Serializaton works", - asJson(contactWithRelayVox), - is(equalTo(jsonFixture("fixtures/contact.relay.voice.json")))); - - assertThat("Contact Relay Video Serializaton works", - asJson(contactWithRelayVid), - is(equalTo(jsonFixture("fixtures/contact.relay.video.json")))); - } - - @Test - public void deserializeFromJSON() throws Exception { - ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"), - "whisper", false, false); - - assertThat("a ClientContact can be deserialized from JSON", - fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class), - is(contact)); - } - - -} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java index 002a6e794..9afbeefe3 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/entities/PreKeyTest.java @@ -6,9 +6,7 @@ package org.whispersystems.textsecuregcm.tests.entities; import org.junit.Test; -import org.whispersystems.textsecuregcm.entities.ClientContact; import org.whispersystems.textsecuregcm.entities.PreKey; -import org.whispersystems.textsecuregcm.util.Util; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -17,16 +15,6 @@ import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.*; public class PreKeyTest { - @Test - public void deserializeFromJSONV() throws Exception { - ClientContact contact = new ClientContact(Util.getContactToken("+14152222222"), - "whisper", false, false); - - assertThat("a ClientContact can be deserialized from JSON", - fromJson(jsonFixture("fixtures/contact.relay.json"), ClientContact.class), - is(contact)); - } - @Test public void serializeToJSONV2() throws Exception { PreKey preKey = new PreKey(1234, "test"); 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 55b8ad100..8b05559ef 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 @@ -13,7 +13,6 @@ import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.AccountsManager; -import org.whispersystems.textsecuregcm.storage.DirectoryManager; import org.whispersystems.textsecuregcm.storage.KeysDynamoDb; import org.whispersystems.textsecuregcm.storage.MessagesManager; import org.whispersystems.textsecuregcm.storage.ProfilesManager; @@ -42,7 +41,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -54,7 +52,7 @@ public class AccountsManagerTest { when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(uuid.toString()); when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}"); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional account = accountsManager.get("+14152222222"); assertTrue(account.isPresent()); @@ -72,7 +70,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -83,7 +80,7 @@ public class AccountsManagerTest { when(commands.get(eq("Account3::" + uuid.toString()))).thenReturn("{\"number\": \"+14152222222\", \"name\": \"test\"}"); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional account = accountsManager.get(uuid); assertTrue(account.isPresent()); @@ -102,7 +99,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -114,7 +110,7 @@ public class AccountsManagerTest { when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional retrieved = accountsManager.get("+14152222222"); assertTrue(retrieved.isPresent()); @@ -134,7 +130,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -146,7 +141,7 @@ public class AccountsManagerTest { when(commands.get(eq("Account3::" + uuid))).thenReturn(null); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional retrieved = accountsManager.get(uuid); assertTrue(retrieved.isPresent()); @@ -166,7 +161,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -178,7 +172,7 @@ public class AccountsManagerTest { when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!")); when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional retrieved = accountsManager.get("+14152222222"); assertTrue(retrieved.isPresent()); @@ -198,7 +192,6 @@ public class AccountsManagerTest { RedisAdvancedClusterCommands commands = mock(RedisAdvancedClusterCommands.class); FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands); Accounts accounts = mock(Accounts.class); - DirectoryManager directoryManager = mock(DirectoryManager.class); DirectoryQueue directoryQueue = mock(DirectoryQueue.class); KeysDynamoDb keysDynamoDb = mock(KeysDynamoDb.class); MessagesManager messagesManager = mock(MessagesManager.class); @@ -210,7 +203,7 @@ public class AccountsManagerTest { when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!")); when(accounts.get(eq(uuid))).thenReturn(Optional.of(account)); - AccountsManager accountsManager = new AccountsManager(accounts, directoryManager, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); + AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager); Optional retrieved = accountsManager.get(uuid); assertTrue(retrieved.isPresent()); 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 a6a69c46a..2e3cd0a05 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 @@ -8,16 +8,12 @@ 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; -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 java.util.Arrays; import java.util.Optional; @@ -25,7 +21,6 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; public class DirectoryReconcilerTest { @@ -39,10 +34,8 @@ public class DirectoryReconcilerTest { private final Account activeAccount = mock(Account.class); private final Account inactiveAccount = mock(Account.class); private final Account undiscoverableAccount = mock(Account.class); - private final BatchOperationHandle batchOperationHandle = mock(BatchOperationHandle.class); - private final DirectoryManager directoryManager = mock(DirectoryManager.class); private final DirectoryReconciliationClient reconciliationClient = mock(DirectoryReconciliationClient.class); - private final DirectoryReconciler directoryReconciler = new DirectoryReconciler("test", true, reconciliationClient, directoryManager); + private final DirectoryReconciler directoryReconciler = new DirectoryReconciler("test", reconciliationClient); private final DirectoryReconciliationResponse successResponse = new DirectoryReconciliationResponse(DirectoryReconciliationResponse.Status.OK); @@ -60,7 +53,6 @@ public class DirectoryReconcilerTest { when(undiscoverableAccount.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER); when(undiscoverableAccount.isEnabled()).thenReturn(true); when(undiscoverableAccount.isDiscoverableByPhoneNumber()).thenReturn(false); - when(directoryManager.startBatchOperation()).thenReturn(batchOperationHandle); } @Test @@ -68,38 +60,12 @@ public class DirectoryReconcilerTest { when(reconciliationClient.sendChunk(any())).thenReturn(successResponse); directoryReconciler.timeAndProcessCrawlChunk(Optional.of(VALID_UUID), Arrays.asList(activeAccount, inactiveAccount, undiscoverableAccount)); - verify(activeAccount, atLeastOnce()).getUuid(); - verify(activeAccount, atLeastOnce()).getNumber(); - verify(activeAccount, atLeastOnce()).isEnabled(); - verify(activeAccount, atLeastOnce()).isDiscoverableByPhoneNumber(); - verify(inactiveAccount, atLeastOnce()).getNumber(); - verify(inactiveAccount, atLeastOnce()).isEnabled(); - verify(undiscoverableAccount, atLeastOnce()).getUuid(); - verify(undiscoverableAccount, atLeastOnce()).getNumber(); - verify(undiscoverableAccount, atLeastOnce()).isEnabled(); - verify(undiscoverableAccount, atLeastOnce()).isDiscoverableByPhoneNumber(); - ArgumentCaptor request = ArgumentCaptor.forClass(DirectoryReconciliationRequest.class); verify(reconciliationClient, times(1)).sendChunk(request.capture()); assertThat(request.getValue().getFromUuid()).isEqualTo(VALID_UUID); assertThat(request.getValue().getToUuid()).isEqualTo(UNDISCOVERABLE_UUID); assertThat(request.getValue().getUsers()).isEqualTo(Arrays.asList(new DirectoryReconciliationRequest.User(VALID_UUID, 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_NUMBERRR)); - verify(directoryManager, times(1)).remove(eq(batchOperationHandle), eq(UNDISCOVERABLE_NUMBER)); - verify(directoryManager, times(1)).stopBatchOperation(eq(batchOperationHandle)); - - assertThat(addedContact.getValue().getToken()).isEqualTo(Util.getContactToken(VALID_NUMBERRR)); - - verifyNoMoreInteractions(activeAccount); - verifyNoMoreInteractions(inactiveAccount); - verifyNoMoreInteractions(batchOperationHandle); - verifyNoMoreInteractions(directoryManager); - verifyNoMoreInteractions(reconciliationClient); } }