Mirror disconnection requests to `DisconnectionRequestManager`

This commit is contained in:
Jon Chambers 2024-11-11 10:25:39 -05:00 committed by Jon Chambers
parent 7e861f388f
commit 3288d3d538
17 changed files with 111 additions and 21 deletions

View File

@ -81,6 +81,7 @@ import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice; import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator; import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.auth.CloudflareTurnCredentialsManager; import org.whispersystems.textsecuregcm.auth.CloudflareTurnCredentialsManager;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager; import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
@ -549,6 +550,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
.virtualExecutorService(name(getClass(), "appleAppStore-%d")); .virtualExecutorService(name(getClass(), "appleAppStore-%d"));
ExecutorService clientEventExecutor = environment.lifecycle() ExecutorService clientEventExecutor = environment.lifecycle()
.virtualExecutorService(name(getClass(), "clientEvent-%d")); .virtualExecutorService(name(getClass(), "clientEvent-%d"));
ExecutorService disconnectionRequestListenerExecutor = environment.lifecycle()
.virtualExecutorService(name(getClass(), "disconnectionRequest-%d"));
ScheduledExecutorService appleAppStoreRetryExecutor = environment.lifecycle() ScheduledExecutorService appleAppStoreRetryExecutor = environment.lifecycle()
.scheduledExecutorService(name(getClass(), "appleAppStoreRetry-%d")).threads(1).build(); .scheduledExecutorService(name(getClass(), "appleAppStoreRetry-%d")).threads(1).build();
@ -597,6 +600,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration()); secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
storageServiceExecutor, storageServiceRetryExecutor, config.getSecureStorageServiceConfiguration()); storageServiceExecutor, storageServiceRetryExecutor, config.getSecureStorageServiceConfiguration());
DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor);
WebSocketConnectionEventManager webSocketConnectionEventManager = new WebSocketConnectionEventManager(messagesCluster, clientEventExecutor); WebSocketConnectionEventManager webSocketConnectionEventManager = new WebSocketConnectionEventManager(messagesCluster, clientEventExecutor);
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster); ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
MessagesCache messagesCache = new MessagesCache(messagesCluster, messageDeliveryScheduler, MessagesCache messagesCache = new MessagesCache(messagesCluster, messageDeliveryScheduler,
@ -615,7 +619,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor); new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keysManager, messagesManager, profilesManager, pubsubClient, accountLockManager, keysManager, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, webSocketConnectionEventManager, secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager, webSocketConnectionEventManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor,
clock, config.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager); clock, config.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs); RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
@ -645,7 +649,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new MessageDeliveryLoopMonitor(rateLimitersCluster); new MessageDeliveryLoopMonitor(rateLimitersCluster);
final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager( final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, webSocketConnectionEventManager, svr2CredentialsGenerator, svr3CredentialsGenerator, accountsManager, disconnectionRequestManager, webSocketConnectionEventManager, svr2CredentialsGenerator, svr3CredentialsGenerator,
registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters); registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters);
final ReportedMessageMetricsListener reportedMessageMetricsListener = new ReportedMessageMetricsListener( final ReportedMessageMetricsListener reportedMessageMetricsListener = new ReportedMessageMetricsListener(
@ -974,7 +978,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.jersey().register(new AuthDynamicFeature(accountAuthFilter)); environment.jersey().register(new AuthDynamicFeature(accountAuthFilter));
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class)); environment.jersey().register(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class));
environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager,
webSocketConnectionEventManager)); disconnectionRequestManager, webSocketConnectionEventManager));
environment.jersey().register(new TimestampResponseFilter()); environment.jersey().register(new TimestampResponseFilter());
/// ///
@ -987,7 +991,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
pushNotificationScheduler, webSocketConnectionEventManager, websocketScheduledExecutor, pushNotificationScheduler, webSocketConnectionEventManager, websocketScheduledExecutor,
messageDeliveryScheduler, clientReleaseManager, messageDeliveryLoopMonitor)); messageDeliveryScheduler, clientReleaseManager, messageDeliveryLoopMonitor));
webSocketEnvironment.jersey() webSocketEnvironment.jersey()
.register(new WebsocketRefreshApplicationEventListener(accountsManager, webSocketConnectionEventManager)); .register(new WebsocketRefreshApplicationEventListener(accountsManager, disconnectionRequestManager, webSocketConnectionEventManager));
webSocketEnvironment.jersey().register(new RateLimitByIpFilter(rateLimiters)); webSocketEnvironment.jersey().register(new RateLimitByIpFilter(rateLimiters));
webSocketEnvironment.jersey().register(new RequestStatisticsFilter(TrafficSource.WEBSOCKET)); webSocketEnvironment.jersey().register(new RequestStatisticsFilter(TrafficSource.WEBSOCKET));
webSocketEnvironment.jersey().register(MultiRecipientMessageProvider.class); webSocketEnvironment.jersey().register(MultiRecipientMessageProvider.class);
@ -1130,7 +1134,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
WebSocketEnvironment<AuthenticatedDevice> provisioningEnvironment = new WebSocketEnvironment<>(environment, WebSocketEnvironment<AuthenticatedDevice> provisioningEnvironment = new WebSocketEnvironment<>(environment,
webSocketEnvironment.getRequestLog(), Duration.ofMillis(60000)); webSocketEnvironment.getRequestLog(), Duration.ofMillis(60000));
provisioningEnvironment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, provisioningEnvironment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager,
webSocketConnectionEventManager)); disconnectionRequestManager, webSocketConnectionEventManager));
provisioningEnvironment.setConnectListener(new ProvisioningConnectListener(provisioningManager)); provisioningEnvironment.setConnectListener(new ProvisioningConnectListener(provisioningManager));
provisioningEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET, clientReleaseManager)); provisioningEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET, clientReleaseManager));
provisioningEnvironment.jersey().register(new KeepAliveController(webSocketConnectionEventManager)); provisioningEnvironment.jersey().register(new KeepAliveController(webSocketConnectionEventManager));

View File

@ -54,6 +54,7 @@ public class RegistrationLockVerificationManager {
private static final String PHONE_VERIFICATION_TYPE_TAG_NAME = "phoneVerificationType"; private static final String PHONE_VERIFICATION_TYPE_TAG_NAME = "phoneVerificationType";
private final AccountsManager accounts; private final AccountsManager accounts;
private final DisconnectionRequestManager disconnectionRequestManager;
private final WebSocketConnectionEventManager webSocketConnectionEventManager; private final WebSocketConnectionEventManager webSocketConnectionEventManager;
private final ExternalServiceCredentialsGenerator svr2CredentialGenerator; private final ExternalServiceCredentialsGenerator svr2CredentialGenerator;
private final ExternalServiceCredentialsGenerator svr3CredentialGenerator; private final ExternalServiceCredentialsGenerator svr3CredentialGenerator;
@ -63,6 +64,7 @@ public class RegistrationLockVerificationManager {
public RegistrationLockVerificationManager( public RegistrationLockVerificationManager(
final AccountsManager accounts, final AccountsManager accounts,
final DisconnectionRequestManager disconnectionRequestManager,
final WebSocketConnectionEventManager webSocketConnectionEventManager, final WebSocketConnectionEventManager webSocketConnectionEventManager,
final ExternalServiceCredentialsGenerator svr2CredentialGenerator, final ExternalServiceCredentialsGenerator svr2CredentialGenerator,
final ExternalServiceCredentialsGenerator svr3CredentialGenerator, final ExternalServiceCredentialsGenerator svr3CredentialGenerator,
@ -70,6 +72,7 @@ public class RegistrationLockVerificationManager {
final PushNotificationManager pushNotificationManager, final PushNotificationManager pushNotificationManager,
final RateLimiters rateLimiters) { final RateLimiters rateLimiters) {
this.accounts = accounts; this.accounts = accounts;
this.disconnectionRequestManager = disconnectionRequestManager;
this.webSocketConnectionEventManager = webSocketConnectionEventManager; this.webSocketConnectionEventManager = webSocketConnectionEventManager;
this.svr2CredentialGenerator = svr2CredentialGenerator; this.svr2CredentialGenerator = svr2CredentialGenerator;
this.svr3CredentialGenerator = svr3CredentialGenerator; this.svr3CredentialGenerator = svr3CredentialGenerator;
@ -162,6 +165,7 @@ public class RegistrationLockVerificationManager {
final List<Byte> deviceIds = updatedAccount.getDevices().stream().map(Device::getId).toList(); final List<Byte> deviceIds = updatedAccount.getDevices().stream().map(Device::getId).toList();
webSocketConnectionEventManager.requestDisconnection(updatedAccount.getUuid(), deviceIds); webSocketConnectionEventManager.requestDisconnection(updatedAccount.getUuid(), deviceIds);
disconnectionRequestManager.requestDisconnection(updatedAccount.getUuid(), deviceIds);
try { try {
// Send a push notification that prompts the client to attempt login and fail due to locked credentials // Send a push notification that prompts the client to attempt login and fail due to locked credentials

View File

@ -20,9 +20,11 @@ public class WebsocketRefreshApplicationEventListener implements ApplicationEven
private final WebsocketRefreshRequestEventListener websocketRefreshRequestEventListener; private final WebsocketRefreshRequestEventListener websocketRefreshRequestEventListener;
public WebsocketRefreshApplicationEventListener(final AccountsManager accountsManager, public WebsocketRefreshApplicationEventListener(final AccountsManager accountsManager,
final DisconnectionRequestManager disconnectionRequestManager,
final WebSocketConnectionEventManager webSocketConnectionEventManager) { final WebSocketConnectionEventManager webSocketConnectionEventManager) {
this.websocketRefreshRequestEventListener = new WebsocketRefreshRequestEventListener( this.websocketRefreshRequestEventListener = new WebsocketRefreshRequestEventListener(
disconnectionRequestManager,
webSocketConnectionEventManager, webSocketConnectionEventManager,
new LinkedDeviceRefreshRequirementProvider(accountsManager), new LinkedDeviceRefreshRequirementProvider(accountsManager),
new PhoneNumberChangeRefreshRequirementProvider(accountsManager)); new PhoneNumberChangeRefreshRequirementProvider(accountsManager));

View File

@ -23,6 +23,7 @@ import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager;
public class WebsocketRefreshRequestEventListener implements RequestEventListener { public class WebsocketRefreshRequestEventListener implements RequestEventListener {
private final DisconnectionRequestManager disconnectionRequestManager;
private final WebSocketConnectionEventManager webSocketConnectionEventManager; private final WebSocketConnectionEventManager webSocketConnectionEventManager;
private final WebsocketRefreshRequirementProvider[] providers; private final WebsocketRefreshRequirementProvider[] providers;
@ -35,9 +36,11 @@ public class WebsocketRefreshRequestEventListener implements RequestEventListene
private static final Logger logger = LoggerFactory.getLogger(WebsocketRefreshRequestEventListener.class); private static final Logger logger = LoggerFactory.getLogger(WebsocketRefreshRequestEventListener.class);
public WebsocketRefreshRequestEventListener( public WebsocketRefreshRequestEventListener(
final DisconnectionRequestManager disconnectionRequestManager,
final WebSocketConnectionEventManager webSocketConnectionEventManager, final WebSocketConnectionEventManager webSocketConnectionEventManager,
final WebsocketRefreshRequirementProvider... providers) { final WebsocketRefreshRequirementProvider... providers) {
this.disconnectionRequestManager = disconnectionRequestManager;
this.webSocketConnectionEventManager = webSocketConnectionEventManager; this.webSocketConnectionEventManager = webSocketConnectionEventManager;
this.providers = providers; this.providers = providers;
} }
@ -61,6 +64,7 @@ public class WebsocketRefreshRequestEventListener implements RequestEventListene
try { try {
displacedDevices.incrementAndGet(); displacedDevices.incrementAndGet();
webSocketConnectionEventManager.requestDisconnection(pair.first(), List.of(pair.second())); webSocketConnectionEventManager.requestDisconnection(pair.first(), List.of(pair.second()));
disconnectionRequestManager.requestDisconnection(pair.first(), List.of(pair.second()));
} catch (final Exception e) { } catch (final Exception e) {
logger.error("Could not displace device presence", e); logger.error("Could not displace device presence", e);
} }

View File

@ -63,6 +63,7 @@ import org.apache.commons.lang3.StringUtils;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException; import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
@ -124,6 +125,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
private final ProfilesManager profilesManager; private final ProfilesManager profilesManager;
private final SecureStorageClient secureStorageClient; private final SecureStorageClient secureStorageClient;
private final SecureValueRecovery2Client secureValueRecovery2Client; private final SecureValueRecovery2Client secureValueRecovery2Client;
private final DisconnectionRequestManager disconnectionRequestManager;
private final WebSocketConnectionEventManager webSocketConnectionEventManager; private final WebSocketConnectionEventManager webSocketConnectionEventManager;
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager; private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final ClientPublicKeysManager clientPublicKeysManager; private final ClientPublicKeysManager clientPublicKeysManager;
@ -202,6 +204,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
final ProfilesManager profilesManager, final ProfilesManager profilesManager,
final SecureStorageClient secureStorageClient, final SecureStorageClient secureStorageClient,
final SecureValueRecovery2Client secureValueRecovery2Client, final SecureValueRecovery2Client secureValueRecovery2Client,
final DisconnectionRequestManager disconnectionRequestManager,
final WebSocketConnectionEventManager webSocketConnectionEventManager, final WebSocketConnectionEventManager webSocketConnectionEventManager,
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager, final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final ClientPublicKeysManager clientPublicKeysManager, final ClientPublicKeysManager clientPublicKeysManager,
@ -219,6 +222,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
this.profilesManager = profilesManager; this.profilesManager = profilesManager;
this.secureStorageClient = secureStorageClient; this.secureStorageClient = secureStorageClient;
this.secureValueRecovery2Client = secureValueRecovery2Client; this.secureValueRecovery2Client = secureValueRecovery2Client;
this.disconnectionRequestManager = disconnectionRequestManager;
this.webSocketConnectionEventManager = webSocketConnectionEventManager; this.webSocketConnectionEventManager = webSocketConnectionEventManager;
this.registrationRecoveryPasswordsManager = requireNonNull(registrationRecoveryPasswordsManager); this.registrationRecoveryPasswordsManager = requireNonNull(registrationRecoveryPasswordsManager);
this.clientPublicKeysManager = clientPublicKeysManager; this.clientPublicKeysManager = clientPublicKeysManager;
@ -326,6 +330,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
messagesManager.clear(aci), messagesManager.clear(aci),
profilesManager.deleteAll(aci)) profilesManager.deleteAll(aci))
.thenCompose(ignored -> webSocketConnectionEventManager.requestDisconnection(aci)) .thenCompose(ignored -> webSocketConnectionEventManager.requestDisconnection(aci))
.thenCompose(ignored -> disconnectionRequestManager.requestDisconnection(aci))
.thenCompose(ignored -> accounts.reclaimAccount(e.getExistingAccount(), account, additionalWriteItems)) .thenCompose(ignored -> accounts.reclaimAccount(e.getExistingAccount(), account, additionalWriteItems))
.thenCompose(ignored -> { .thenCompose(ignored -> {
// We should have cleared all messages before overwriting the old account, but more may have arrived // We should have cleared all messages before overwriting the old account, but more may have arrived
@ -590,6 +595,7 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
.whenComplete((ignored, throwable) -> { .whenComplete((ignored, throwable) -> {
if (throwable == null) { if (throwable == null) {
webSocketConnectionEventManager.requestDisconnection(accountIdentifier, List.of(deviceId)); webSocketConnectionEventManager.requestDisconnection(accountIdentifier, List.of(deviceId));
disconnectionRequestManager.requestDisconnection(accountIdentifier, List.of(deviceId));
} }
}); });
} }
@ -1236,7 +1242,10 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
registrationRecoveryPasswordsManager.removeForNumber(account.getNumber())) registrationRecoveryPasswordsManager.removeForNumber(account.getNumber()))
.thenCompose(ignored -> accounts.delete(account.getUuid(), additionalWriteItems)) .thenCompose(ignored -> accounts.delete(account.getUuid(), additionalWriteItems))
.thenCompose(ignored -> redisDeleteAsync(account)) .thenCompose(ignored -> redisDeleteAsync(account))
.thenRun(() -> webSocketConnectionEventManager.requestDisconnection(account.getUuid())); .thenRun(() -> {
webSocketConnectionEventManager.requestDisconnection(account.getUuid());
disconnectionRequestManager.requestDisconnection(account.getUuid());
});
} }
private String getAccountMapKey(String key) { private String getAccountMapKey(String key) {

View File

@ -23,6 +23,7 @@ import org.signal.libsignal.zkgroup.GenericServerSecretParams;
import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.InvalidInputException;
import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator; import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.backup.BackupManager; import org.whispersystems.textsecuregcm.backup.BackupManager;
import org.whispersystems.textsecuregcm.backup.BackupsDb; import org.whispersystems.textsecuregcm.backup.BackupsDb;
@ -136,6 +137,8 @@ record CommandDependencies(
.maxThreads(16).minThreads(16).build(); .maxThreads(16).minThreads(16).build();
ExecutorService clientEventExecutor = environment.lifecycle() ExecutorService clientEventExecutor = environment.lifecycle()
.virtualExecutorService(name(name, "clientEvent-%d")); .virtualExecutorService(name(name, "clientEvent-%d"));
ExecutorService disconnectionRequestListenerExecutor = environment.lifecycle()
.virtualExecutorService(name(name, "disconnectionRequest-%d"));
ScheduledExecutorService secureValueRecoveryServiceRetryExecutor = environment.lifecycle() ScheduledExecutorService secureValueRecoveryServiceRetryExecutor = environment.lifecycle()
.scheduledExecutorService(name(name, "secureValueRecoveryServiceRetry-%d")).threads(1).build(); .scheduledExecutorService(name(name, "secureValueRecoveryServiceRetry-%d")).threads(1).build();
@ -205,6 +208,7 @@ record CommandDependencies(
configuration.getSvr2Configuration()); configuration.getSvr2Configuration());
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator, SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
storageServiceExecutor, storageServiceRetryExecutor, configuration.getSecureStorageServiceConfiguration()); storageServiceExecutor, storageServiceRetryExecutor, configuration.getSecureStorageServiceConfiguration());
DisconnectionRequestManager disconnectionRequestManager = new DisconnectionRequestManager(pubsubClient, disconnectionRequestListenerExecutor);
WebSocketConnectionEventManager webSocketConnectionEventManager = new WebSocketConnectionEventManager(messagesCluster, clientEventExecutor); WebSocketConnectionEventManager webSocketConnectionEventManager = new WebSocketConnectionEventManager(messagesCluster, clientEventExecutor);
MessagesCache messagesCache = new MessagesCache(messagesCluster, MessagesCache messagesCache = new MessagesCache(messagesCluster,
messageDeliveryScheduler, messageDeletionExecutor, Clock.systemUTC(), dynamicConfigurationManager); messageDeliveryScheduler, messageDeletionExecutor, Clock.systemUTC(), dynamicConfigurationManager);
@ -222,7 +226,7 @@ record CommandDependencies(
new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor); new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster, AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keys, messagesManager, profilesManager, pubsubClient, accountLockManager, keys, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, webSocketConnectionEventManager, secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager, webSocketConnectionEventManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor,
clock, configuration.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager); clock, configuration.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RateLimiters rateLimiters = RateLimiters.createAndValidate(configuration.getLimitsConfiguration(), RateLimiters rateLimiters = RateLimiters.createAndValidate(configuration.getLimitsConfiguration(),

View File

@ -95,6 +95,7 @@ class LinkedDeviceRefreshRequirementProviderTest {
.build(); .build();
private AccountsManager accountsManager; private AccountsManager accountsManager;
private DisconnectionRequestManager disconnectionRequestManager;
private WebSocketConnectionEventManager webSocketConnectionEventManager; private WebSocketConnectionEventManager webSocketConnectionEventManager;
private LinkedDeviceRefreshRequirementProvider provider; private LinkedDeviceRefreshRequirementProvider provider;
@ -102,12 +103,13 @@ class LinkedDeviceRefreshRequirementProviderTest {
@BeforeEach @BeforeEach
void setup() { void setup() {
accountsManager = mock(AccountsManager.class); accountsManager = mock(AccountsManager.class);
disconnectionRequestManager = mock(DisconnectionRequestManager.class);
webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
provider = new LinkedDeviceRefreshRequirementProvider(accountsManager); provider = new LinkedDeviceRefreshRequirementProvider(accountsManager);
final WebsocketRefreshRequestEventListener listener = final WebsocketRefreshRequestEventListener listener =
new WebsocketRefreshRequestEventListener(webSocketConnectionEventManager, provider); new WebsocketRefreshRequestEventListener(disconnectionRequestManager, webSocketConnectionEventManager, provider);
when(applicationEventListener.onRequest(any())).thenReturn(listener); when(applicationEventListener.onRequest(any())).thenReturn(listener);
@ -142,6 +144,10 @@ class LinkedDeviceRefreshRequirementProviderTest {
verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 1)); verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 1));
verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 2)); verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 2));
verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 3)); verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of((byte) 3));
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of((byte) 1));
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of((byte) 2));
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of((byte) 3));
} }
@ParameterizedTest @ParameterizedTest
@ -170,6 +176,7 @@ class LinkedDeviceRefreshRequirementProviderTest {
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
initialDeviceIds.forEach(deviceId -> { initialDeviceIds.forEach(deviceId -> {
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of(deviceId));
verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of(deviceId)); verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of(deviceId));
}); });

View File

@ -74,7 +74,11 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
private static final AccountAuthenticator AUTHENTICATOR = mock(AccountAuthenticator.class); private static final AccountAuthenticator AUTHENTICATOR = mock(AccountAuthenticator.class);
private static final AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class); private static final AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class);
private static final WebSocketConnectionEventManager PUBSUB_CLIENT_PRESENCE = mock(WebSocketConnectionEventManager.class); private static final DisconnectionRequestManager DISCONNECTION_REQUEST_MANAGER =
mock(DisconnectionRequestManager.class);
private static final WebSocketConnectionEventManager WEB_SOCKET_CONNECTION_EVENT_MANAGER =
mock(WebSocketConnectionEventManager.class);
private WebSocketClient client; private WebSocketClient client;
private final Account account1 = new Account(); private final Account account1 = new Account();
@ -84,7 +88,7 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
reset(AUTHENTICATOR, ACCOUNTS_MANAGER, PUBSUB_CLIENT_PRESENCE); reset(AUTHENTICATOR, ACCOUNTS_MANAGER, WEB_SOCKET_CONNECTION_EVENT_MANAGER);
client = new WebSocketClient(); client = new WebSocketClient();
client.start(); client.start();
@ -123,9 +127,9 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); .addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
webSocketEnvironment.jersey().register(new RemoteAddressFilter()); webSocketEnvironment.jersey().register(new RemoteAddressFilter());
webSocketEnvironment.jersey() webSocketEnvironment.jersey()
.register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, PUBSUB_CLIENT_PRESENCE)); .register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, DISCONNECTION_REQUEST_MANAGER, WEB_SOCKET_CONNECTION_EVENT_MANAGER));
environment.jersey() environment.jersey()
.register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, PUBSUB_CLIENT_PRESENCE)); .register(new WebsocketRefreshApplicationEventListener(ACCOUNTS_MANAGER, DISCONNECTION_REQUEST_MANAGER, WEB_SOCKET_CONNECTION_EVENT_MANAGER));
webSocketEnvironment.setConnectListener(webSocketSessionContext -> { webSocketEnvironment.setConnectListener(webSocketSessionContext -> {
}); });
@ -199,7 +203,7 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
// Event listeners can fire after responses are sent // Event listeners can fire after responses are sent
verify(ACCOUNTS_MANAGER, timeout(5000).times(1)).getByAccountIdentifier(eq(account1.getUuid())); verify(ACCOUNTS_MANAGER, timeout(5000).times(1)).getByAccountIdentifier(eq(account1.getUuid()));
verifyNoMoreInteractions(PUBSUB_CLIENT_PRESENCE); verifyNoMoreInteractions(WEB_SOCKET_CONNECTION_EVENT_MANAGER);
verifyNoMoreInteractions(ACCOUNTS_MANAGER); verifyNoMoreInteractions(ACCOUNTS_MANAGER);
} }
@ -213,9 +217,14 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
// Make sure we disconnect the account if the account has changed numbers. Event listeners can fire after responses // Make sure we disconnect the account if the account has changed numbers. Event listeners can fire after responses
// are sent, so use a timeout. // are sent, so use a timeout.
verify(PUBSUB_CLIENT_PRESENCE, timeout(5000)) verify(DISCONNECTION_REQUEST_MANAGER, timeout(5000))
.requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId())); .requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId()));
verifyNoMoreInteractions(PUBSUB_CLIENT_PRESENCE); verifyNoMoreInteractions(DISCONNECTION_REQUEST_MANAGER);
verify(WEB_SOCKET_CONNECTION_EVENT_MANAGER, timeout(5000))
.requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId()));
verifyNoMoreInteractions(WEB_SOCKET_CONNECTION_EVENT_MANAGER);
} }
@Test @Test
@ -229,9 +238,13 @@ class PhoneNumberChangeRefreshRequirementProviderTest {
// Make sure we disconnect the account if the account has changed numbers. Event listeners can fire after responses // Make sure we disconnect the account if the account has changed numbers. Event listeners can fire after responses
// are sent, so use a timeout. // are sent, so use a timeout.
verify(PUBSUB_CLIENT_PRESENCE, timeout(5000)) verify(DISCONNECTION_REQUEST_MANAGER, timeout(5000))
.requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId())); .requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId()));
verifyNoMoreInteractions(PUBSUB_CLIENT_PRESENCE); verifyNoMoreInteractions(DISCONNECTION_REQUEST_MANAGER);
verify(WEB_SOCKET_CONNECTION_EVENT_MANAGER, timeout(5000))
.requestDisconnection(account1.getUuid(), List.of(authenticatedDevice.getId()));
verifyNoMoreInteractions(WEB_SOCKET_CONNECTION_EVENT_MANAGER);
} }
@ParameterizedTest @ParameterizedTest

View File

@ -46,6 +46,7 @@ import org.whispersystems.textsecuregcm.util.Pair;
class RegistrationLockVerificationManagerTest { class RegistrationLockVerificationManagerTest {
private final AccountsManager accountsManager = mock(AccountsManager.class); private final AccountsManager accountsManager = mock(AccountsManager.class);
private final DisconnectionRequestManager disconnectionRequestManager = mock(DisconnectionRequestManager.class);
private final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); private final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
private final ExternalServiceCredentialsGenerator svr2CredentialsGenerator = mock( private final ExternalServiceCredentialsGenerator svr2CredentialsGenerator = mock(
ExternalServiceCredentialsGenerator.class); ExternalServiceCredentialsGenerator.class);
@ -56,8 +57,8 @@ class RegistrationLockVerificationManagerTest {
private static PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class); private static PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class);
private final RateLimiters rateLimiters = mock(RateLimiters.class); private final RateLimiters rateLimiters = mock(RateLimiters.class);
private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager( private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, webSocketConnectionEventManager, svr2CredentialsGenerator, svr3CredentialsGenerator, accountsManager, disconnectionRequestManager, webSocketConnectionEventManager, svr2CredentialsGenerator,
registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters); svr3CredentialsGenerator, registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters);
private final RateLimiter pinLimiter = mock(RateLimiter.class); private final RateLimiter pinLimiter = mock(RateLimiter.class);
@ -107,6 +108,7 @@ class RegistrationLockVerificationManagerTest {
} else { } else {
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber()); verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
} }
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of(Device.PRIMARY_ID));
verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of(Device.PRIMARY_ID)); verify(webSocketConnectionEventManager).requestDisconnection(account.getUuid(), List.of(Device.PRIMARY_ID));
try { try {
verify(pushNotificationManager).sendAttemptLoginNotification(any(), eq("failedRegistrationLock")); verify(pushNotificationManager).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
@ -130,6 +132,7 @@ class RegistrationLockVerificationManagerTest {
verify(pushNotificationManager, never()).sendAttemptLoginNotification(any(), eq("failedRegistrationLock")); verify(pushNotificationManager, never()).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
} catch (NotPushRegisteredException npre) {} } catch (NotPushRegisteredException npre) {}
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber()); verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
verify(webSocketConnectionEventManager, never()).requestDisconnection(any(), any()); verify(webSocketConnectionEventManager, never()).requestDisconnection(any(), any());
}); });
} }
@ -168,6 +171,7 @@ class RegistrationLockVerificationManagerTest {
verify(account, never()).lockAuthTokenHash(); verify(account, never()).lockAuthTokenHash();
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber()); verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
verify(webSocketConnectionEventManager, never()).requestDisconnection(any(), any()); verify(webSocketConnectionEventManager, never()).requestDisconnection(any(), any());
} }

View File

@ -60,6 +60,7 @@ import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice; import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener; import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId; import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
@ -110,6 +111,7 @@ class DeviceControllerTest {
private static final Account account = mock(Account.class); private static final Account account = mock(Account.class);
private static final Account maxedAccount = mock(Account.class); private static final Account maxedAccount = mock(Account.class);
private static final Device primaryDevice = mock(Device.class); private static final Device primaryDevice = mock(Device.class);
private static final DisconnectionRequestManager disconnectionRequestManager = mock(DisconnectionRequestManager.class);
private static final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); private static final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
private static final Map<String, Integer> deviceConfiguration = new HashMap<>(); private static final Map<String, Integer> deviceConfiguration = new HashMap<>();
private static final TestClock testClock = TestClock.now(); private static final TestClock testClock = TestClock.now();
@ -131,7 +133,7 @@ class DeviceControllerTest {
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class)) .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class))
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new WebsocketRefreshApplicationEventListener(accountsManager, webSocketConnectionEventManager)) .addProvider(new WebsocketRefreshApplicationEventListener(accountsManager, disconnectionRequestManager, webSocketConnectionEventManager))
.addProvider(new DeviceLimitExceededExceptionMapper()) .addProvider(new DeviceLimitExceededExceptionMapper())
.addResource(deviceController) .addResource(deviceController)
.build(); .build();

View File

@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil;
@ -36,6 +37,7 @@ import org.junitpioneer.jupiter.cartesian.CartesianTest;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId; import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
@ -77,6 +79,8 @@ public class AccountCreationDeletionIntegrationTest {
private AccountsManager accountsManager; private AccountsManager accountsManager;
private KeysManager keysManager; private KeysManager keysManager;
private ClientPublicKeysManager clientPublicKeysManager; private ClientPublicKeysManager clientPublicKeysManager;
private WebSocketConnectionEventManager webSocketConnectionEventManager;
private DisconnectionRequestManager disconnectionRequestManager;
record DeliveryChannels(boolean fetchesMessages, String apnsToken, String fcmToken) {} record DeliveryChannels(boolean fetchesMessages, String apnsToken, String fcmToken) {}
@ -138,10 +142,13 @@ public class AccountCreationDeletionIntegrationTest {
when(registrationRecoveryPasswordsManager.removeForNumber(any())) when(registrationRecoveryPasswordsManager.removeForNumber(any()))
.thenReturn(CompletableFuture.completedFuture(null)); .thenReturn(CompletableFuture.completedFuture(null));
final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
when(webSocketConnectionEventManager.requestDisconnection(any())) when(webSocketConnectionEventManager.requestDisconnection(any()))
.thenReturn(CompletableFuture.completedFuture(null)); .thenReturn(CompletableFuture.completedFuture(null));
disconnectionRequestManager = mock(DisconnectionRequestManager.class);
when(disconnectionRequestManager.requestDisconnection(any())).thenReturn(CompletableFuture.completedFuture(null));
accountsManager = new AccountsManager( accountsManager = new AccountsManager(
accounts, accounts,
phoneNumberIdentifiers, phoneNumberIdentifiers,
@ -153,6 +160,7 @@ public class AccountCreationDeletionIntegrationTest {
profilesManager, profilesManager,
secureStorageClient, secureStorageClient,
svr2Client, svr2Client,
disconnectionRequestManager,
webSocketConnectionEventManager, webSocketConnectionEventManager,
registrationRecoveryPasswordsManager, registrationRecoveryPasswordsManager,
clientPublicKeysManager, clientPublicKeysManager,
@ -399,6 +407,9 @@ public class AccountCreationDeletionIntegrationTest {
pniPqLastResortPreKey); pniPqLastResortPreKey);
assertEquals(existingAccountUuid, reregisteredAccount.getUuid()); assertEquals(existingAccountUuid, reregisteredAccount.getUuid());
verify(webSocketConnectionEventManager).requestDisconnection(existingAccountUuid);
verify(disconnectionRequestManager).requestDisconnection(existingAccountUuid);
} }
@Test @Test
@ -472,6 +483,9 @@ public class AccountCreationDeletionIntegrationTest {
assertFalse(keysManager.getLastResort(account.getUuid(), Device.PRIMARY_ID).join().isPresent()); assertFalse(keysManager.getLastResort(account.getUuid(), Device.PRIMARY_ID).join().isPresent());
assertFalse(keysManager.getLastResort(account.getPhoneNumberIdentifier(), Device.PRIMARY_ID).join().isPresent()); assertFalse(keysManager.getLastResort(account.getPhoneNumberIdentifier(), Device.PRIMARY_ID).join().isPresent());
assertFalse(clientPublicKeysManager.findPublicKey(account.getUuid(), Device.PRIMARY_ID).join().isPresent()); assertFalse(clientPublicKeysManager.findPublicKey(account.getUuid(), Device.PRIMARY_ID).join().isPresent());
verify(webSocketConnectionEventManager).requestDisconnection(aci);
verify(disconnectionRequestManager).requestDisconnection(aci);
} }
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")

View File

@ -31,6 +31,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException; import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
@ -66,6 +67,7 @@ class AccountsManagerChangeNumberIntegrationTest {
static final RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build(); static final RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
private KeysManager keysManager; private KeysManager keysManager;
private DisconnectionRequestManager disconnectionRequestManager;
private WebSocketConnectionEventManager webSocketConnectionEventManager; private WebSocketConnectionEventManager webSocketConnectionEventManager;
private ExecutorService accountLockExecutor; private ExecutorService accountLockExecutor;
@ -117,6 +119,7 @@ class AccountsManagerChangeNumberIntegrationTest {
when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null)); when(svr2Client.deleteBackups(any())).thenReturn(CompletableFuture.completedFuture(null));
webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
disconnectionRequestManager = mock(DisconnectionRequestManager.class);
final PhoneNumberIdentifiers phoneNumberIdentifiers = final PhoneNumberIdentifiers phoneNumberIdentifiers =
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName()); new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName());
@ -144,6 +147,7 @@ class AccountsManagerChangeNumberIntegrationTest {
profilesManager, profilesManager,
secureStorageClient, secureStorageClient,
svr2Client, svr2Client,
disconnectionRequestManager,
webSocketConnectionEventManager, webSocketConnectionEventManager,
registrationRecoveryPasswordsManager, registrationRecoveryPasswordsManager,
clientPublicKeysManager, clientPublicKeysManager,
@ -275,6 +279,7 @@ class AccountsManagerChangeNumberIntegrationTest {
assertEquals(secondNumber, accountsManager.getByAccountIdentifier(originalUuid).map(Account::getNumber).orElseThrow()); assertEquals(secondNumber, accountsManager.getByAccountIdentifier(originalUuid).map(Account::getNumber).orElseThrow());
verify(webSocketConnectionEventManager).requestDisconnection(existingAccountUuid); verify(webSocketConnectionEventManager).requestDisconnection(existingAccountUuid);
verify(disconnectionRequestManager).requestDisconnection(existingAccountUuid);
assertEquals(Optional.of(existingAccountUuid), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber)); assertEquals(Optional.of(existingAccountUuid), accountsManager.findRecentlyDeletedAccountIdentifier(originalNumber));
assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber)); assertEquals(Optional.empty(), accountsManager.findRecentlyDeletedAccountIdentifier(secondNumber));

View File

@ -43,6 +43,7 @@ import org.mockito.stubbing.Answer;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil; import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
@ -133,6 +134,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
mock(ProfilesManager.class), mock(ProfilesManager.class),
mock(SecureStorageClient.class), mock(SecureStorageClient.class),
mock(SecureValueRecovery2Client.class), mock(SecureValueRecovery2Client.class),
mock(DisconnectionRequestManager.class),
mock(WebSocketConnectionEventManager.class), mock(WebSocketConnectionEventManager.class),
mock(RegistrationRecoveryPasswordsManager.class), mock(RegistrationRecoveryPasswordsManager.class),
mock(ClientPublicKeysManager.class), mock(ClientPublicKeysManager.class),

View File

@ -11,6 +11,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.entities.RestoreAccountRequest; import org.whispersystems.textsecuregcm.entities.RestoreAccountRequest;
import org.whispersystems.textsecuregcm.entities.RemoteAttachment; import org.whispersystems.textsecuregcm.entities.RemoteAttachment;
import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.identity.IdentityType;
@ -62,6 +63,7 @@ public class AccountsManagerDeviceTransferIntegrationTest {
mock(ProfilesManager.class), mock(ProfilesManager.class),
mock(SecureStorageClient.class), mock(SecureStorageClient.class),
mock(SecureValueRecovery2Client.class), mock(SecureValueRecovery2Client.class),
mock(DisconnectionRequestManager.class),
mock(WebSocketConnectionEventManager.class), mock(WebSocketConnectionEventManager.class),
mock(RegistrationRecoveryPasswordsManager.class), mock(RegistrationRecoveryPasswordsManager.class),
mock(ClientPublicKeysManager.class), mock(ClientPublicKeysManager.class),

View File

@ -70,6 +70,7 @@ import org.mockito.stubbing.Answer;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil; import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException; import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
@ -117,6 +118,7 @@ class AccountsManagerTest {
private KeysManager keysManager; private KeysManager keysManager;
private MessagesManager messagesManager; private MessagesManager messagesManager;
private ProfilesManager profilesManager; private ProfilesManager profilesManager;
private DisconnectionRequestManager disconnectionRequestManager;
private WebSocketConnectionEventManager webSocketConnectionEventManager; private WebSocketConnectionEventManager webSocketConnectionEventManager;
private ClientPublicKeysManager clientPublicKeysManager; private ClientPublicKeysManager clientPublicKeysManager;
@ -152,6 +154,7 @@ class AccountsManagerTest {
keysManager = mock(KeysManager.class); keysManager = mock(KeysManager.class);
messagesManager = mock(MessagesManager.class); messagesManager = mock(MessagesManager.class);
profilesManager = mock(ProfilesManager.class); profilesManager = mock(ProfilesManager.class);
disconnectionRequestManager = mock(DisconnectionRequestManager.class);
webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
clientPublicKeysManager = mock(ClientPublicKeysManager.class); clientPublicKeysManager = mock(ClientPublicKeysManager.class);
dynamicConfiguration = mock(DynamicConfiguration.class); dynamicConfiguration = mock(DynamicConfiguration.class);
@ -241,6 +244,8 @@ class AccountsManagerTest {
when(webSocketConnectionEventManager.requestDisconnection(any())) when(webSocketConnectionEventManager.requestDisconnection(any()))
.thenReturn(CompletableFuture.completedFuture(null)); .thenReturn(CompletableFuture.completedFuture(null));
when(disconnectionRequestManager.requestDisconnection(any())).thenReturn(CompletableFuture.completedFuture(null));
accountsManager = new AccountsManager( accountsManager = new AccountsManager(
accounts, accounts,
phoneNumberIdentifiers, phoneNumberIdentifiers,
@ -252,6 +257,7 @@ class AccountsManagerTest {
profilesManager, profilesManager,
storageClient, storageClient,
svr2Client, svr2Client,
disconnectionRequestManager,
webSocketConnectionEventManager, webSocketConnectionEventManager,
registrationRecoveryPasswordsManager, registrationRecoveryPasswordsManager,
clientPublicKeysManager, clientPublicKeysManager,
@ -879,6 +885,7 @@ class AccountsManagerTest {
verify(messagesManager, times(2)).clear(existingUuid); verify(messagesManager, times(2)).clear(existingUuid);
verify(profilesManager, times(2)).deleteAll(existingUuid); verify(profilesManager, times(2)).deleteAll(existingUuid);
verify(webSocketConnectionEventManager).requestDisconnection(existingUuid); verify(webSocketConnectionEventManager).requestDisconnection(existingUuid);
verify(disconnectionRequestManager).requestDisconnection(existingUuid);
} }
@Test @Test

View File

@ -34,6 +34,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager; import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClient;
@ -137,6 +138,9 @@ class AccountsManagerUsernameIntegrationTest {
final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class); final WebSocketConnectionEventManager webSocketConnectionEventManager = mock(WebSocketConnectionEventManager.class);
when(webSocketConnectionEventManager.requestDisconnection(any())).thenReturn(CompletableFuture.completedFuture(null)); when(webSocketConnectionEventManager.requestDisconnection(any())).thenReturn(CompletableFuture.completedFuture(null));
final DisconnectionRequestManager disconnectionRequestManager = mock(DisconnectionRequestManager.class);
when(disconnectionRequestManager.requestDisconnection(any())).thenReturn(CompletableFuture.completedFuture(null));
accountsManager = new AccountsManager( accountsManager = new AccountsManager(
accounts, accounts,
phoneNumberIdentifiers, phoneNumberIdentifiers,
@ -148,6 +152,7 @@ class AccountsManagerUsernameIntegrationTest {
profileManager, profileManager,
mock(SecureStorageClient.class), mock(SecureStorageClient.class),
mock(SecureValueRecovery2Client.class), mock(SecureValueRecovery2Client.class),
disconnectionRequestManager,
webSocketConnectionEventManager, webSocketConnectionEventManager,
mock(RegistrationRecoveryPasswordsManager.class), mock(RegistrationRecoveryPasswordsManager.class),
mock(ClientPublicKeysManager.class), mock(ClientPublicKeysManager.class),

View File

@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.DisconnectionRequestManager;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.DeviceInfo; import org.whispersystems.textsecuregcm.entities.DeviceInfo;
import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.identity.IdentityType;
@ -149,6 +150,7 @@ public class AddRemoveDeviceIntegrationTest {
profilesManager, profilesManager,
secureStorageClient, secureStorageClient,
svr2Client, svr2Client,
mock(DisconnectionRequestManager.class),
mock(WebSocketConnectionEventManager.class), mock(WebSocketConnectionEventManager.class),
registrationRecoveryPasswordsManager, registrationRecoveryPasswordsManager,
clientPublicKeysManager, clientPublicKeysManager,