Move /v1/svrb/auth to /v1/archives/auth/svrb

This commit is contained in:
ravi-signal
2025-08-01 12:00:44 -05:00
committed by GitHub
parent f8d27d8fab
commit e8a1854c5e
23 changed files with 243 additions and 189 deletions

View File

@@ -93,6 +93,7 @@ import org.whispersystems.textsecuregcm.backup.BackupManager;
import org.whispersystems.textsecuregcm.backup.BackupsDb;
import org.whispersystems.textsecuregcm.backup.Cdn3BackupCredentialGenerator;
import org.whispersystems.textsecuregcm.backup.Cdn3RemoteStorageManager;
import org.whispersystems.textsecuregcm.backup.SecureValueRecoveryBCredentialsGeneratorFactory;
import org.whispersystems.textsecuregcm.badges.ConfiguredProfileBadgeConverter;
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
import org.whispersystems.textsecuregcm.captcha.CaptchaClient;
@@ -126,7 +127,6 @@ import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
import org.whispersystems.textsecuregcm.controllers.RemoteConfigControllerV1;
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecoveryBController;
import org.whispersystems.textsecuregcm.controllers.StickerController;
import org.whispersystems.textsecuregcm.controllers.SubscriptionController;
import org.whispersystems.textsecuregcm.controllers.VerificationController;
@@ -595,9 +595,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ExternalServiceCredentialsGenerator paymentsCredentialsGenerator = PaymentsController.credentialsGenerator(
config.getPaymentsServiceConfiguration());
ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
config.getSvr2Configuration());
ExternalServiceCredentialsGenerator svrbCredentialsGenerator = SecureValueRecoveryBController.credentialsGenerator(
config.getSvrbConfiguration());
config.getSvr2Configuration());
ExternalServiceCredentialsGenerator svrbCredentialsGenerator =
SecureValueRecoveryBCredentialsGeneratorFactory.svrbCredentialsGenerator(config.getSvrbConfiguration());
RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
@@ -644,7 +644,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new ClientPublicKeysManager(clientPublicKeys, accountLockManager, accountLockExecutor);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keysManager, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, secureValueRecoveryBClient, disconnectionRequestManager,
secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, messagePollExecutor,
clock, config.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
@@ -798,6 +798,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
tusAttachmentGenerator,
cdn3BackupCredentialGenerator,
cdn3RemoteStorageManager,
svrbCredentialsGenerator,
secureValueRecoveryBClient,
clock);
final AppleDeviceChecks appleDeviceChecks = new AppleDeviceChecks(
@@ -1115,7 +1117,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
new SecureStorageController(storageCredentialsGenerator),
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
new SecureValueRecoveryBController(svrbCredentialsGenerator),
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
config.getCdnConfiguration().bucket()),

View File

@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.backup;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.util.DataSize;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
@@ -19,6 +18,7 @@ import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -38,9 +38,12 @@ import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.attachments.AttachmentGenerator;
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecoveryClient;
import org.whispersystems.textsecuregcm.util.AsyncTimerUtil;
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
import org.whispersystems.textsecuregcm.util.Pair;
@@ -94,9 +97,10 @@ public class BackupManager {
private final Cdn3BackupCredentialGenerator cdn3BackupCredentialGenerator;
private final RemoteStorageManager remoteStorageManager;
private final SecureRandom secureRandom = new SecureRandom();
private final ExternalServiceCredentialsGenerator secureValueRecoveryBCredentialsGenerator;
private final SecureValueRecoveryClient secureValueRecoveryBClient;
private final Clock clock;
public BackupManager(
final BackupsDb backupsDb,
final GenericServerSecretParams serverSecretParams,
@@ -104,6 +108,8 @@ public class BackupManager {
final TusAttachmentGenerator tusAttachmentGenerator,
final Cdn3BackupCredentialGenerator cdn3BackupCredentialGenerator,
final RemoteStorageManager remoteStorageManager,
final ExternalServiceCredentialsGenerator secureValueRecoveryBCredentialsGenerator,
final SecureValueRecoveryClient secureValueRecoveryBClient,
final Clock clock) {
this.backupsDb = backupsDb;
this.serverSecretParams = serverSecretParams;
@@ -111,7 +117,9 @@ public class BackupManager {
this.tusAttachmentGenerator = tusAttachmentGenerator;
this.cdn3BackupCredentialGenerator = cdn3BackupCredentialGenerator;
this.remoteStorageManager = remoteStorageManager;
this.secureValueRecoveryBClient = secureValueRecoveryBClient;
this.clock = clock;
this.secureValueRecoveryBCredentialsGenerator = secureValueRecoveryBCredentialsGenerator;
}
@@ -387,6 +395,26 @@ public class BackupManager {
return cdn3BackupCredentialGenerator.readHeaders(backupUser.backupDir());
}
/**
* Generate credentials that can be used with SVRB
*
* @param backupUser an already ZK authenticated backup user
* @return the credential that may be used with SVRB
*/
public ExternalServiceCredentials generateSvrbAuth(final AuthenticatedBackupUser backupUser) {
checkBackupLevel(backupUser, BackupLevel.FREE);
// Clients may only use SVRB with their messages backup-id
checkBackupCredentialType(backupUser, BackupCredentialType.MESSAGES);
return secureValueRecoveryBCredentialsGenerator.generateFor(svrbIdentifier(backupUser));
}
private static String svrbIdentifier(final AuthenticatedBackupUser backupUser) {
return svrbIdentifier(BackupsDb.hashedBackupId(backupUser.backupId()));
}
private static String svrbIdentifier(final byte[] hashedBackupId) {
return HexFormat.of().formatHex(hashedBackupId);
}
/**
* List of media stored for a particular backup id
@@ -427,13 +455,19 @@ public class BackupManager {
public CompletableFuture<Void> deleteEntireBackup(final AuthenticatedBackupUser backupUser) {
checkBackupLevel(backupUser, BackupLevel.FREE);
return backupsDb
// Clients only include SVRB data with their messages backup-id
final CompletableFuture<Void> svrbRemoval = switch(backupUser.credentialType()) {
case BackupCredentialType.MESSAGES -> secureValueRecoveryBClient.removeData(svrbIdentifier(backupUser));
case BackupCredentialType.MEDIA -> CompletableFuture.completedFuture(null);
};
return svrbRemoval.thenCompose(_ -> backupsDb
// Try to swap out the backupDir for the user
.scheduleBackupDeletion(backupUser)
// If there was already a pending swap, try to delete the cdn objects directly
.exceptionallyCompose(ExceptionUtils.exceptionallyHandler(BackupsDb.PendingDeletionException.class, e ->
AsyncTimerUtil.record(SYNCHRONOUS_DELETE_TIMER, () ->
deletePrefix(backupUser.backupDir(), DELETION_CONCURRENCY))));
deletePrefix(backupUser.backupDir(), DELETION_CONCURRENCY)))));
}
@@ -617,12 +651,17 @@ public class BackupManager {
* @return A stage that completes when the deletion operation is finished
*/
public CompletableFuture<Void> expireBackup(final ExpiredBackup expiredBackup) {
return backupsDb.startExpiration(expiredBackup)
// Clients only include SVRB data with their messages backup-id
final CompletableFuture<Void> svrbRemoval = switch(expiredBackup.expirationType()) {
case ALL -> secureValueRecoveryBClient.removeData(svrbIdentifier(expiredBackup.hashedBackupId()));
case MEDIA, GARBAGE_COLLECTION -> CompletableFuture.completedFuture(null);
};
return svrbRemoval.thenCompose(_ -> backupsDb.startExpiration(expiredBackup)
// the deletion operation is effectively single threaded -- it's expected that the caller can increase
// concurrency by deleting more backups at once, rather than increasing concurrency deleting an individual
// backup
.thenCompose(ignored -> deletePrefix(expiredBackup.prefixToDelete(), 1))
.thenCompose(ignored -> backupsDb.finishExpiration(expiredBackup));
.thenCompose(ignored -> backupsDb.finishExpiration(expiredBackup)));
}
/**

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.backup;
import com.google.common.annotations.VisibleForTesting;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
import java.time.Clock;
public class SecureValueRecoveryBCredentialsGeneratorFactory {
private SecureValueRecoveryBCredentialsGeneratorFactory() {}
@VisibleForTesting
static ExternalServiceCredentialsGenerator svrbCredentialsGenerator(
final SecureValueRecoveryConfiguration cfg,
final Clock clock) {
return ExternalServiceCredentialsGenerator
.builder(cfg.userAuthenticationTokenSharedSecret())
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
.prependUsername(false)
.withDerivedUsernameTruncateLength(16)
.withClock(clock)
.build();
}
public static ExternalServiceCredentialsGenerator svrbCredentialsGenerator(final SecureValueRecoveryConfiguration cfg) {
return svrbCredentialsGenerator(cfg, Clock.systemUTC());
}
}

View File

@@ -64,6 +64,7 @@ import org.signal.libsignal.zkgroup.backups.BackupAuthCredentialRequest;
import org.signal.libsignal.zkgroup.backups.BackupCredentialType;
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.backup.BackupAuthManager;
import org.whispersystems.textsecuregcm.backup.BackupManager;
import org.whispersystems.textsecuregcm.backup.CopyParameters;
@@ -389,6 +390,35 @@ public class ArchiveController {
.thenApply(ReadAuthResponse::new);
}
@GET
@Path("/auth/svrb")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Generate credentials for SVRB",
description = """
Generate SVRB service credentials. Generated credentials have an expiration time of 1 day (subject to change)
""")
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
@ApiResponseZkAuth
public CompletionStage<ExternalServiceCredentials> svrbAuth(
@Auth final Optional<AuthenticatedDevice> account,
@HeaderParam(HttpHeaders.USER_AGENT) final String userAgent,
@Parameter(description = BackupAuthCredentialPresentationHeader.DESCRIPTION, schema = @Schema(implementation = String.class))
@NotNull
@HeaderParam(X_SIGNAL_ZK_AUTH) final ArchiveController.BackupAuthCredentialPresentationHeader presentation,
@Parameter(description = BackupAuthCredentialPresentationSignature.DESCRIPTION, schema = @Schema(implementation = String.class))
@NotNull
@HeaderParam(X_SIGNAL_ZK_AUTH_SIGNATURE) final BackupAuthCredentialPresentationSignature signature) {
if (account.isPresent()) {
throw new BadRequestException("must not use authenticated connection for anonymous operations");
}
return backupManager
.authenticateBackupUser(presentation.presentation, signature.signature, userAgent)
.thenApply(backupManager::generateSvrbAuth);
}
public record BackupInfoResponse(
@Schema(description = "The CDN type where the message backup is stored. Media may be stored elsewhere.")
int cdn,

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.controllers;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.time.Clock;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecoveryConfiguration;
@Path("/v1/svrb")
@Tag(name = "Secure Value Recovery B")
public class SecureValueRecoveryBController {
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg) {
return credentialsGenerator(cfg, Clock.systemUTC());
}
@VisibleForTesting
public static ExternalServiceCredentialsGenerator credentialsGenerator(final SecureValueRecoveryConfiguration cfg,
final Clock clock) {
return ExternalServiceCredentialsGenerator
.builder(cfg.userAuthenticationTokenSharedSecret())
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
.prependUsername(false)
.withDerivedUsernameTruncateLength(16)
.withClock(clock)
.build();
}
private final ExternalServiceCredentialsGenerator svrbCredentialGenerator;
public SecureValueRecoveryBController(final ExternalServiceCredentialsGenerator svrbCredentialGenerator) {
this.svrbCredentialGenerator = svrbCredentialGenerator;
}
@GET
@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Operation(
summary = "Generate credentials for SVRB",
description = """
Generate SVRB service credentials. Generated credentials have an expiration time of 1 day (subject to change)
"""
)
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
public ExternalServiceCredentials getAuth(@Auth final AuthenticatedDevice auth) {
return svrbCredentialGenerator.generateFor(auth.accountIdentifier().toString());
}
}

View File

@@ -21,6 +21,8 @@ import org.signal.chat.backup.GetBackupInfoRequest;
import org.signal.chat.backup.GetBackupInfoResponse;
import org.signal.chat.backup.GetCdnCredentialsRequest;
import org.signal.chat.backup.GetCdnCredentialsResponse;
import org.signal.chat.backup.GetSvrBCredentialsRequest;
import org.signal.chat.backup.GetSvrBCredentialsResponse;
import org.signal.chat.backup.GetUploadFormRequest;
import org.signal.chat.backup.GetUploadFormResponse;
import org.signal.chat.backup.ListMediaRequest;
@@ -64,6 +66,16 @@ public class BackupsAnonymousGrpcService extends ReactorBackupsAnonymousGrpc.Bac
.map(credentials -> GetCdnCredentialsResponse.newBuilder().putAllHeaders(credentials).build());
}
@Override
public Mono<GetSvrBCredentialsResponse> getSvrBCredentials(final GetSvrBCredentialsRequest request) {
return authenticateBackupUserMono(request.getSignedPresentation())
.map(backupManager::generateSvrbAuth)
.map(credentials -> GetSvrBCredentialsResponse.newBuilder()
.setUsername(credentials.username())
.setPassword(credentials.password())
.build());
}
@Override
public Mono<GetBackupInfoResponse> getBackupInfo(final GetBackupInfoRequest request) {
return Mono.fromFuture(() ->

View File

@@ -47,16 +47,6 @@ enum ExternalServiceDefinitions {
.withClock(clock)
.build();
}),
SVRB(ExternalServiceType.EXTERNAL_SERVICE_TYPE_SVRB, (chatConfig, clock) -> {
final SecureValueRecoveryConfiguration cfg = chatConfig.getSvrbConfiguration();
return ExternalServiceCredentialsGenerator
.builder(cfg.userAuthenticationTokenSharedSecret())
.withUserDerivationKey(cfg.userIdTokenSharedSecret().value())
.prependUsername(false)
.withDerivedUsernameTruncateLength(16)
.withClock(clock)
.build();
}),
STORAGE(ExternalServiceType.EXTERNAL_SERVICE_TYPE_STORAGE, (chatConfig, clock) -> {
final PaymentsServiceConfiguration cfg = chatConfig.getPaymentsServiceConfiguration();
return ExternalServiceCredentialsGenerator

View File

@@ -30,9 +30,10 @@ import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
import org.whispersystems.textsecuregcm.util.HttpUtils;
/**
* A client for sending requests to Signal's secure value recovery v2 service on behalf of authenticated users.
* A client for sending requests to Signal's secure value recovery service on behalf of authenticated users.
*/
public class SecureValueRecoveryClient {
private static final Logger logger = LoggerFactory.getLogger(SecureValueRecoveryClient.class);
private final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator;
@@ -67,8 +68,12 @@ public class SecureValueRecoveryClient {
}
public CompletableFuture<Void> removeData(final UUID accountUuid) {
return removeData(accountUuid.toString());
}
final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateForUuid(accountUuid);
public CompletableFuture<Void> removeData(final String userIdentifier) {
final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateFor(userIdentifier);
final HttpRequest request = HttpRequest.newBuilder()
.uri(deleteUri)
@@ -83,11 +88,11 @@ public class SecureValueRecoveryClient {
final List<Integer> allowedErrors = allowedDeletionErrorStatusCodes.get();
if (allowedErrors.contains(response.statusCode())) {
logger.warn("Ignoring failure to delete svr entry for account {} with status {}",
accountUuid, response.statusCode());
logger.warn("Ignoring failure to delete svr entry for identifier {} with status {}",
userIdentifier, response.statusCode());
return null;
}
logger.warn("Failed to delete svr entry for account {} with status {}", accountUuid, response.statusCode());
logger.warn("Failed to delete svr entry for identifier {} with status {}", userIdentifier, response.statusCode());
throw new SecureValueRecoveryException("Failed to delete backup", String.valueOf(response.statusCode()));
});
}

View File

@@ -128,7 +128,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
private final ProfilesManager profilesManager;
private final SecureStorageClient secureStorageClient;
private final SecureValueRecoveryClient secureValueRecovery2Client;
private final SecureValueRecoveryClient secureValueRecoveryBClient;
private final DisconnectionRequestManager disconnectionRequestManager;
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final ClientPublicKeysManager clientPublicKeysManager;
@@ -219,7 +218,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
final ProfilesManager profilesManager,
final SecureStorageClient secureStorageClient,
final SecureValueRecoveryClient secureValueRecovery2Client,
final SecureValueRecoveryClient secureValueRecoveryBClient,
final DisconnectionRequestManager disconnectionRequestManager,
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final ClientPublicKeysManager clientPublicKeysManager,
@@ -238,7 +236,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
this.profilesManager = profilesManager;
this.secureStorageClient = secureStorageClient;
this.secureValueRecovery2Client = secureValueRecovery2Client;
this.secureValueRecoveryBClient = secureValueRecoveryBClient;
this.disconnectionRequestManager = disconnectionRequestManager;
this.registrationRecoveryPasswordsManager = requireNonNull(registrationRecoveryPasswordsManager);
this.clientPublicKeysManager = clientPublicKeysManager;
@@ -1232,7 +1229,6 @@ public class AccountsManager extends RedisPubSubAdapter<String, String> implemen
return CompletableFuture.allOf(
secureStorageClient.deleteStoredData(account.getUuid()),
secureValueRecovery2Client.removeData(account.getUuid()),
secureValueRecoveryBClient.removeData(account.getUuid()),
keysManager.deleteSingleUsePreKeys(account.getUuid()),
keysManager.deleteSingleUsePreKeys(account.getPhoneNumberIdentifier()),
messagesManager.clear(account.getUuid()),

View File

@@ -30,11 +30,11 @@ import org.whispersystems.textsecuregcm.backup.BackupManager;
import org.whispersystems.textsecuregcm.backup.BackupsDb;
import org.whispersystems.textsecuregcm.backup.Cdn3BackupCredentialGenerator;
import org.whispersystems.textsecuregcm.backup.Cdn3RemoteStorageManager;
import org.whispersystems.textsecuregcm.backup.SecureValueRecoveryBCredentialsGeneratorFactory;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecovery2Controller;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.controllers.SecureValueRecoveryBController;
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples;
import org.whispersystems.textsecuregcm.grpc.net.GrpcClientConnectionManager;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
@@ -174,8 +174,8 @@ record CommandDependencies(
configuration.getSecureStorageServiceConfiguration());
ExternalServiceCredentialsGenerator secureValueRecovery2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
configuration.getSvr2Configuration());
ExternalServiceCredentialsGenerator secureValueRecoveryBCredentialsGenerator = SecureValueRecoveryBController.credentialsGenerator(
configuration.getSvrbConfiguration());
ExternalServiceCredentialsGenerator secureValueRecoveryBCredentialsGenerator =
SecureValueRecoveryBCredentialsGeneratorFactory.svrbCredentialsGenerator(configuration.getSvrbConfiguration());
final ExecutorService awsSdkMetricsExecutor = environment.lifecycle()
.virtualExecutorService(MetricRegistry.name(WhisperServerService.class, "awsSdkMetrics-%d"));
@@ -276,7 +276,7 @@ record CommandDependencies(
new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords);
AccountsManager accountsManager = new AccountsManager(accounts, phoneNumberIdentifiers, cacheCluster,
pubsubClient, accountLockManager, keys, messagesManager, profilesManager,
secureStorageClient, secureValueRecovery2Client, secureValueRecoveryBClient, disconnectionRequestManager,
secureStorageClient, secureValueRecovery2Client, disconnectionRequestManager,
registrationRecoveryPasswordsManager, clientPublicKeysManager, accountLockExecutor, messagePollExecutor,
clock, configuration.getLinkDeviceSecretConfiguration().secret().value(), dynamicConfigurationManager);
RateLimiters rateLimiters = RateLimiters.create(dynamicConfigurationManager, rateLimitersCluster);
@@ -299,6 +299,8 @@ record CommandDependencies(
remoteStorageHttpExecutor,
remoteStorageRetryExecutor,
configuration.getCdn3StorageManagerConfiguration()),
secureValueRecoveryBCredentialsGenerator,
secureValueRecoveryBClient,
clock);
final IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(