Add `enabled` to SVR2 configuration
This commit is contained in:
parent
4fa08fb189
commit
0cc84131de
|
@ -146,6 +146,7 @@ directoryV2:
|
||||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with CDS to generate auth identity tokens for Signal users
|
||||||
|
|
||||||
svr2:
|
svr2:
|
||||||
|
enabled: false
|
||||||
uri: svr2.example.com
|
uri: svr2.example.com
|
||||||
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth tokens for Signal users
|
||||||
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
userIdTokenSharedSecret: bbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # base64-encoded secret shared with SVR2 to generate auth identity tokens for Signal users
|
||||||
|
|
|
@ -796,7 +796,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||||
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
new SecureBackupController(backupCredentialsGenerator, accountsManager),
|
||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator),
|
new SecureValueRecovery2Controller(svr2CredentialsGenerator, config.getSvr2Configuration()),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
||||||
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(),
|
||||||
config.getCdnConfiguration().getBucket()),
|
config.getCdnConfiguration().getBucket()),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
@ -12,9 +12,10 @@ import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||||
|
|
||||||
public record SecureValueRecovery2Configuration(
|
public record SecureValueRecovery2Configuration(
|
||||||
|
boolean enabled,
|
||||||
|
@NotBlank String uri,
|
||||||
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
@ExactlySize({32}) byte[] userAuthenticationTokenSharedSecret,
|
||||||
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
@ExactlySize({32}) byte[] userIdTokenSharedSecret,
|
||||||
@NotBlank String uri,
|
|
||||||
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
@NotEmpty List<@NotBlank String> svrCaCertificates,
|
||||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||||
@NotNull @Valid RetryConfiguration retry) {
|
@NotNull @Valid RetryConfiguration retry) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
@ -32,9 +33,12 @@ public class SecureValueRecovery2Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator;
|
private final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator;
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
public SecureValueRecovery2Controller(final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator) {
|
public SecureValueRecovery2Controller(final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator,
|
||||||
|
final SecureValueRecovery2Configuration cfg) {
|
||||||
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
||||||
|
this.enabled = cfg.enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -51,6 +55,9 @@ public class SecureValueRecovery2Controller {
|
||||||
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
|
@ApiResponse(responseCode = "200", description = "`JSON` with generated credentials.", useReturnTypeSchema = true)
|
||||||
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
|
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
|
||||||
public ExternalServiceCredentials getAuth(@Auth final AuthenticatedAccount auth) {
|
public ExternalServiceCredentials getAuth(@Auth final AuthenticatedAccount auth) {
|
||||||
|
if (!enabled) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
return backupServiceCredentialGenerator.generateFor(auth.getAccount().getUuid().toString());
|
return backupServiceCredentialGenerator.generateFor(auth.getAccount().getUuid().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public class SecureValueRecovery2Client {
|
||||||
private final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator;
|
private final ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator;
|
||||||
private final URI deleteUri;
|
private final URI deleteUri;
|
||||||
private final FaultTolerantHttpClient httpClient;
|
private final FaultTolerantHttpClient httpClient;
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String DELETE_PATH = "/v1/delete";
|
static final String DELETE_PATH = "/v1/delete";
|
||||||
|
@ -52,9 +53,15 @@ public class SecureValueRecovery2Client {
|
||||||
.withSecurityProtocol(FaultTolerantHttpClient.SECURITY_PROTOCOL_TLS_1_2)
|
.withSecurityProtocol(FaultTolerantHttpClient.SECURITY_PROTOCOL_TLS_1_2)
|
||||||
.withTrustedServerCertificates(configuration.svrCaCertificates().toArray(new String[0]))
|
.withTrustedServerCertificates(configuration.svrCaCertificates().toArray(new String[0]))
|
||||||
.build();
|
.build();
|
||||||
|
this.enabled = configuration.enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> deleteBackups(final UUID accountUuid) {
|
public CompletableFuture<Void> deleteBackups(final UUID accountUuid) {
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateForUuid(accountUuid);
|
final ExternalServiceCredentials credentials = secureValueRecoveryCredentialsGenerator.generateForUuid(accountUuid);
|
||||||
|
|
||||||
final HttpRequest request = HttpRequest.newBuilder()
|
final HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 Signal Messenger, LLC
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.delete;
|
import static com.github.tomakehurst.wiremock.client.WireMock.delete;
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
||||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
@ -50,8 +51,11 @@ class SecureValueRecovery2ClientTest {
|
||||||
credentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
credentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
|
||||||
httpExecutor = Executors.newSingleThreadExecutor();
|
httpExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(new byte[0], new byte[0],
|
final SecureValueRecovery2Configuration config = new SecureValueRecovery2Configuration(true,
|
||||||
"http://localhost:" + wireMock.getPort(), List.of("""
|
"http://localhost:" + wireMock.getPort(),
|
||||||
|
new byte[0], new byte[0],
|
||||||
|
// This is a randomly-generated, throwaway certificate that's not actually connected to anything
|
||||||
|
List.of("""
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEL
|
MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEL
|
||||||
MAkGA1UECAwCVVMxHjAcBgNVBAoMFVNpZ25hbCBNZXNzZW5nZXIsIExMQzETMBEG
|
MAkGA1UECAwCVVMxHjAcBgNVBAoMFVNpZ25hbCBNZXNzZW5nZXIsIExMQzETMBEG
|
||||||
|
@ -67,8 +71,7 @@ class SecureValueRecovery2ClientTest {
|
||||||
y7MTM4NoBV1k0zb5LAk89SIDPr/maW5AsLtEomzjnEiomjoMBUdNe3YCgQReoLnr
|
y7MTM4NoBV1k0zb5LAk89SIDPr/maW5AsLtEomzjnEiomjoMBUdNe3YCgQReoLnr
|
||||||
R/QaUNbrCjTGYfBsjGbIzmkWPUyTec2ZdRyJ8JiVl386+6CZkxnndQ==
|
R/QaUNbrCjTGYfBsjGbIzmkWPUyTec2ZdRyJ8JiVl386+6CZkxnndQ==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
""",
|
""", """
|
||||||
"""
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEpDCCAowCCQC43PUTWSADVjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
|
MIIEpDCCAowCCQC43PUTWSADVjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
|
||||||
b2NhbGhvc3QwHhcNMjIxMDE3MjA0NTM0WhcNMjMxMDE3MjA0NTM0WjAUMRIwEAYD
|
b2NhbGhvc3QwHhcNMjIxMDE3MjA0NTM0WhcNMjMxMDE3MjA0NTM0WjAUMRIwEAYD
|
||||||
|
@ -96,7 +99,8 @@ class SecureValueRecovery2ClientTest {
|
||||||
WIOjZOKGW690ESKCKOnFjUHVO0HpuWnT81URTuY62FXsYdVc2wE4v0E04mEbqQ0P
|
WIOjZOKGW690ESKCKOnFjUHVO0HpuWnT81URTuY62FXsYdVc2wE4v0E04mEbqQ0P
|
||||||
lY6ZKNA81Lm3YADYtObmK1IUrOPo9BeIaPy0UM08SmN880Vunqa91Q==
|
lY6ZKNA81Lm3YADYtObmK1IUrOPo9BeIaPy0UM08SmN880Vunqa91Q==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
"""), null, null);
|
"""),
|
||||||
|
null, null);
|
||||||
|
|
||||||
secureValueRecovery2Client = new SecureValueRecovery2Client(credentialsGenerator, httpExecutor, config);
|
secureValueRecovery2Client = new SecureValueRecovery2Client(credentialsGenerator, httpExecutor, config);
|
||||||
}
|
}
|
||||||
|
@ -119,8 +123,7 @@ class SecureValueRecovery2ClientTest {
|
||||||
.withBasicAuth(username, password)
|
.withBasicAuth(username, password)
|
||||||
.willReturn(aResponse().withStatus(202)));
|
.willReturn(aResponse().withStatus(202)));
|
||||||
|
|
||||||
// We're happy as long as this doesn't throw an exception
|
assertDoesNotThrow(() -> secureValueRecovery2Client.deleteBackups(accountUuid).join());
|
||||||
secureValueRecovery2Client.deleteBackups(accountUuid).join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue