Add support to trial Cloudflare TURN beta
This commit is contained in:
parent
0986ce12e6
commit
4a28ab6317
|
@ -91,6 +91,8 @@ currentReportingKey.secret: AAAAAAAAAAA=
|
|||
currentReportingKey.salt: AAAAAAAAAAA=
|
||||
|
||||
turn.secret: AAAAAAAAAAA=
|
||||
turn.cloudflare.username: ABCDEFGHIJKLM
|
||||
turn.cloudflare.password: NOPQRSTUVWXYZ
|
||||
|
||||
linkDevice.secret: AAAAAAAAAAA=
|
||||
|
||||
|
|
|
@ -452,6 +452,11 @@ registrationService:
|
|||
|
||||
turn:
|
||||
secret: secret://turn.secret
|
||||
cloudflare:
|
||||
username: secret://turn.cloudflare.username
|
||||
password: secret://turn.cloudflare.password
|
||||
urls:
|
||||
- turns:turn.cloudflare.example.com:443?transport=tcp
|
||||
|
||||
linkDevice:
|
||||
secret: secret://linkDevice.secret
|
||||
|
|
|
@ -57,7 +57,7 @@ import org.whispersystems.textsecuregcm.configuration.SpamFilterConfiguration;
|
|||
import org.whispersystems.textsecuregcm.configuration.StripeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SubscriptionConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.TlsKeyStoreConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.TurnSecretConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.TurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.UnidentifiedDeliveryConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.VirtualThreadConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ZkConfig;
|
||||
|
@ -288,7 +288,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private TurnSecretConfiguration turn;
|
||||
private TurnConfiguration turn;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
|
@ -529,7 +529,7 @@ public class WhisperServerConfiguration extends Configuration {
|
|||
return registrationService;
|
||||
}
|
||||
|
||||
public TurnSecretConfiguration getTurnSecretConfiguration() {
|
||||
public TurnConfiguration getTurnConfiguration() {
|
||||
return turn;
|
||||
}
|
||||
|
||||
|
|
|
@ -605,7 +605,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
pushLatencyManager);
|
||||
final ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
|
||||
final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager,
|
||||
config.getTurnSecretConfiguration().secret().value());
|
||||
config.getTurnConfiguration().secret().value(), config.getTurnConfiguration().cloudflare());
|
||||
|
||||
final CardinalityEstimator messageByteLimitCardinalityEstimator = new CardinalityEstimator(
|
||||
rateLimitersCluster,
|
||||
|
@ -938,7 +938,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, tusAttachmentGenerator,
|
||||
experimentEnrollmentManager),
|
||||
new ArchiveController(backupAuthManager, backupManager),
|
||||
new CallRoutingController(rateLimiters, callRouter, turnTokenGenerator),
|
||||
new CallRoutingController(rateLimiters, callRouter, turnTokenGenerator, experimentEnrollmentManager),
|
||||
new CallLinkController(rateLimiters, callingGenericZkSecretParams),
|
||||
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().certificate().value(),
|
||||
config.getDeliveryCertificate().ecPrivateKey(), config.getDeliveryCertificate().expiresDays()),
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public record TurnToken(String username, String password, List<String> urls, List<String> urlsWithIps, String hostname) {
|
||||
public record TurnToken(String username, String password, List<String> urls, @Nullable List<String> urlsWithIps,
|
||||
@Nullable String hostname) {
|
||||
public TurnToken(String username, String password, List<String> urls) {
|
||||
this(username, password, urls, null, null);
|
||||
}
|
||||
|
|
|
@ -5,17 +5,6 @@
|
|||
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnServerOptions;
|
||||
import org.whispersystems.textsecuregcm.configuration.TurnUriConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicTurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
import org.whispersystems.textsecuregcm.util.WeightedRandomSelect;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
@ -25,6 +14,17 @@ import java.util.Base64;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnServerOptions;
|
||||
import org.whispersystems.textsecuregcm.configuration.CloudflareTurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.TurnUriConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicTurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
import org.whispersystems.textsecuregcm.util.WeightedRandomSelect;
|
||||
|
||||
public class TurnTokenGenerator {
|
||||
|
||||
|
@ -38,13 +38,22 @@ public class TurnTokenGenerator {
|
|||
|
||||
private static final String WithIpsProtocol = "01";
|
||||
|
||||
private final String cloudflareTurnUsername;
|
||||
private final String cloudflareTurnPassword;
|
||||
private final List<String> cloudflareTurnUrls;
|
||||
|
||||
public TurnTokenGenerator(final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
||||
final byte[] turnSecret) {
|
||||
final byte[] turnSecret, final CloudflareTurnConfiguration cloudflareTurnConfiguration) {
|
||||
|
||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||
this.turnSecret = turnSecret;
|
||||
|
||||
this.cloudflareTurnUsername = cloudflareTurnConfiguration.username().value();
|
||||
this.cloudflareTurnPassword = cloudflareTurnConfiguration.password().value();
|
||||
this.cloudflareTurnUrls = cloudflareTurnConfiguration.urls();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TurnToken generate(final UUID aci) {
|
||||
return generateToken(null, null, urls(aci));
|
||||
}
|
||||
|
@ -53,6 +62,10 @@ public class TurnTokenGenerator {
|
|||
return generateToken(options.hostname(), options.urlsWithIps(), options.urlsWithHostname());
|
||||
}
|
||||
|
||||
public TurnToken generateForCloudflareBeta() {
|
||||
return new TurnToken(cloudflareTurnUsername, cloudflareTurnPassword, cloudflareTurnUrls);
|
||||
}
|
||||
|
||||
private TurnToken generateToken(String hostname, List<String> urlsWithIps, List<String> urlsWithHostname) {
|
||||
try {
|
||||
final Mac mac = Mac.getInstance(ALGORITHM);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public record CloudflareTurnConfiguration(@NotNull SecretString username, @NotNull SecretString password,
|
||||
@Valid @NotNull List<@NotBlank String> urls) {
|
||||
|
||||
}
|
|
@ -7,5 +7,5 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
|
||||
public record TurnSecretConfiguration(SecretBytes secret) {
|
||||
public record TurnConfiguration(SecretBytes secret, CloudflareTurnConfiguration cloudflare) {
|
||||
}
|
|
@ -95,7 +95,8 @@ public class AccountController {
|
|||
this.usernameHashZkProofVerifier = usernameHashZkProofVerifier;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
// may be removed after 2024-07-16
|
||||
@Deprecated(forRemoval = true)
|
||||
@GET
|
||||
@Path("/turn/")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import io.dropwizard.auth.Auth;
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Optional;
|
||||
|
@ -21,14 +22,13 @@ import org.slf4j.LoggerFactory;
|
|||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnServerOptions;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnCallRouter;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnServerOptions;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.filters.RemoteAddressFilter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.websocket.auth.ReadOnly;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
@Path("/v1/calling")
|
||||
@io.swagger.v3.oas.annotations.tags.Tag(name = "Calling")
|
||||
public class CallRoutingController {
|
||||
|
@ -39,15 +39,18 @@ public class CallRoutingController {
|
|||
private final RateLimiters rateLimiters;
|
||||
private final TurnCallRouter turnCallRouter;
|
||||
private final TurnTokenGenerator tokenGenerator;
|
||||
private final ExperimentEnrollmentManager experimentEnrollmentManager;
|
||||
|
||||
public CallRoutingController(
|
||||
final RateLimiters rateLimiters,
|
||||
final TurnCallRouter turnCallRouter,
|
||||
final TurnTokenGenerator tokenGenerator
|
||||
final TurnTokenGenerator tokenGenerator,
|
||||
final ExperimentEnrollmentManager experimentEnrollmentManager
|
||||
) {
|
||||
this.rateLimiters = rateLimiters;
|
||||
this.turnCallRouter = turnCallRouter;
|
||||
this.tokenGenerator = tokenGenerator;
|
||||
this.experimentEnrollmentManager = experimentEnrollmentManager;
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -63,7 +66,7 @@ public class CallRoutingController {
|
|||
@ApiResponse(responseCode = "400", description = "Invalid get call endpoint request.")
|
||||
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
|
||||
@ApiResponse(responseCode = "422", description = "Invalid request format.")
|
||||
@ApiResponse(responseCode = "429", description = "Ratelimited.")
|
||||
@ApiResponse(responseCode = "429", description = "Rate limited.")
|
||||
public TurnToken getCallingRelays(
|
||||
final @ReadOnly @Auth AuthenticatedAccount auth,
|
||||
@Context ContainerRequestContext requestContext
|
||||
|
@ -71,6 +74,10 @@ public class CallRoutingController {
|
|||
UUID aci = auth.getAccount().getUuid();
|
||||
rateLimiters.getCallEndpointLimiter().validate(aci);
|
||||
|
||||
if (experimentEnrollmentManager.isEnrolled(aci, "cloudflareTurn")) {
|
||||
return tokenGenerator.generateForCloudflareBeta();
|
||||
}
|
||||
|
||||
Optional<InetAddress> address = Optional.empty();
|
||||
try {
|
||||
final String remoteAddress = (String) requestContext.getProperty(
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.CloudflareTurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
public class TurnTokenGeneratorTest {
|
||||
|
||||
private static final CloudflareTurnConfiguration CLOUDFLARE_TURN_CONFIGURATION = new CloudflareTurnConfiguration(
|
||||
new SecretString("cf_username"), new SecretString("cf_password"), List.of("turn:cloudflare.example.com"));
|
||||
|
||||
@Test
|
||||
public void testAlwaysSelectFirst() throws JsonProcessingException {
|
||||
final String configString = """
|
||||
|
@ -30,7 +35,7 @@ public class TurnTokenGeneratorTest {
|
|||
- uris:
|
||||
- never.org
|
||||
weight: 0
|
||||
""";
|
||||
""";
|
||||
DynamicConfiguration config = DynamicConfigurationManager
|
||||
.parseConfiguration(configString, DynamicConfiguration.class)
|
||||
.orElseThrow();
|
||||
|
@ -42,7 +47,8 @@ public class TurnTokenGeneratorTest {
|
|||
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
|
||||
|
||||
final TurnTokenGenerator turnTokenGenerator =
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8),
|
||||
CLOUDFLARE_TURN_CONFIGURATION);
|
||||
|
||||
final long COUNT = 1000;
|
||||
|
||||
|
@ -83,7 +89,8 @@ public class TurnTokenGeneratorTest {
|
|||
|
||||
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
|
||||
final TurnTokenGenerator turnTokenGenerator =
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8),
|
||||
CLOUDFLARE_TURN_CONFIGURATION);
|
||||
|
||||
final long COUNT = 1000;
|
||||
|
||||
|
@ -126,7 +133,8 @@ public class TurnTokenGeneratorTest {
|
|||
when(mockDynamicConfigManager.getConfiguration()).thenReturn(config);
|
||||
|
||||
final TurnTokenGenerator turnTokenGenerator =
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8));
|
||||
new TurnTokenGenerator(mockDynamicConfigManager, "bloop".getBytes(StandardCharsets.UTF_8),
|
||||
CLOUDFLARE_TURN_CONFIGURATION);
|
||||
|
||||
TurnToken token = turnTokenGenerator.generate(UUID.fromString("732506d7-d04f-43a4-b1d7-8a3a91ebe8a6"));
|
||||
assertThat(token.urls().get(0)).isEqualTo("enrolled.org");
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import io.dropwizard.auth.AuthValueFactoryProvider;
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
|
@ -24,6 +23,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -32,7 +32,10 @@ import org.whispersystems.textsecuregcm.auth.TurnToken;
|
|||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnCallRouter;
|
||||
import org.whispersystems.textsecuregcm.calls.routing.TurnServerOptions;
|
||||
import org.whispersystems.textsecuregcm.configuration.CloudflareTurnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
|
||||
|
@ -43,30 +46,44 @@ import org.whispersystems.textsecuregcm.util.TestRemoteAddressFilterProvider;
|
|||
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class CallRoutingControllerTest {
|
||||
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||
private static final RateLimiter getCallEndpointLimiter = mock(RateLimiter.class);
|
||||
private static final DynamicConfigurationManager<DynamicConfiguration> configManager = mock(DynamicConfigurationManager.class);
|
||||
private static final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(configManager, "bloop".getBytes(
|
||||
StandardCharsets.UTF_8));
|
||||
private static final TurnCallRouter turnCallRouter = mock(TurnCallRouter.class);
|
||||
|
||||
private static final String GET_CALL_ENDPOINTS_PATH = "v1/calling/relays";
|
||||
private static final String REMOTE_ADDRESS = "123.123.123.1";
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
|
||||
.addProvider(new RateLimitExceededExceptionMapper())
|
||||
.addProvider(new TestRemoteAddressFilterProvider(REMOTE_ADDRESS))
|
||||
.setMapper(SystemMapper.jsonMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new CallRoutingController(rateLimiters, turnCallRouter, turnTokenGenerator))
|
||||
.build();
|
||||
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||
private static final RateLimiter getCallEndpointLimiter = mock(RateLimiter.class);
|
||||
private static final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(
|
||||
DynamicConfigurationManager.class);
|
||||
private static final ExperimentEnrollmentManager experimentEnrollmentManager = mock(
|
||||
ExperimentEnrollmentManager.class);
|
||||
private static final TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager,
|
||||
"bloop".getBytes(StandardCharsets.UTF_8),
|
||||
new CloudflareTurnConfiguration(new SecretString("cf_username"), new SecretString("cf_password"),
|
||||
List.of("turn:cf.example.com")));
|
||||
private static final TurnCallRouter turnCallRouter = mock(TurnCallRouter.class);
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
|
||||
.addProvider(new RateLimitExceededExceptionMapper())
|
||||
.addProvider(new TestRemoteAddressFilterProvider(REMOTE_ADDRESS))
|
||||
.setMapper(SystemMapper.jsonMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new CallRoutingController(rateLimiters, turnCallRouter, turnTokenGenerator,
|
||||
experimentEnrollmentManager))
|
||||
.build();
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(rateLimiters.getCallEndpointLimiter()).thenReturn(getCallEndpointLimiter);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
reset(experimentEnrollmentManager, dynamicConfigurationManager, rateLimiters, getCallEndpointLimiter,
|
||||
turnCallRouter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTurnEndpointsSuccess() throws UnknownHostException {
|
||||
TurnServerOptions options = new TurnServerOptions(
|
||||
|
@ -96,6 +113,27 @@ class CallRoutingControllerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTurnEndpointsCloudflare() {
|
||||
when(experimentEnrollmentManager.isEnrolled(AuthHelper.VALID_UUID, "cloudflareTurn"))
|
||||
.thenReturn(true);
|
||||
|
||||
try (Response response = resources.getJerseyTest()
|
||||
.target(GET_CALL_ENDPOINTS_PATH)
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get()) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
TurnToken token = response.readEntity(TurnToken.class);
|
||||
assertThat(token.username()).isNotEmpty();
|
||||
assertThat(token.password()).isNotEmpty();
|
||||
assertThat(token.hostname()).isNull();
|
||||
assertThat(token.urlsWithIps()).isNull();
|
||||
assertThat(token.urls()).isEqualTo(List.of("turn:cf.example.com"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTurnEndpointsInvalidIpSuccess() throws UnknownHostException {
|
||||
TurnServerOptions options = new TurnServerOptions(
|
||||
|
|
|
@ -124,6 +124,8 @@ currentReportingKey.secret: AAAAAAAAAAA=
|
|||
currentReportingKey.salt: AAAAAAAAAAA=
|
||||
|
||||
turn.secret: AAAAAAAAAAA=
|
||||
turn.cloudflare.username: ABCDEFGHIJKLM
|
||||
turn.cloudflare.password: NOPQRSTUVWXYZ
|
||||
|
||||
linkDevice.secret: AAAAAAAAAAA=
|
||||
|
||||
|
|
|
@ -443,6 +443,11 @@ registrationService:
|
|||
|
||||
turn:
|
||||
secret: secret://turn.secret
|
||||
cloudflare:
|
||||
username: secret://turn.cloudflare.username
|
||||
password: secret://turn.cloudflare.password
|
||||
urls:
|
||||
- turns:turn.cloudflare.example.com:443?transport=tcp
|
||||
|
||||
linkDevice:
|
||||
secret: secret://linkDevice.secret
|
||||
|
|
Loading…
Reference in New Issue