From 09ce79bd43ddc72f8647d643d390b79fb105d94d Mon Sep 17 00:00:00 2001 From: Adel Lahlou Date: Fri, 7 Feb 2025 10:58:10 -0800 Subject: [PATCH] Remove /v1/calling/relays API endpoint --- .../textsecuregcm/WhisperServerService.java | 2 - .../controllers/CallRoutingController.java | 103 ---------- .../controllers/CallRoutingControllerV2.java | 2 +- .../CallRoutingControllerTest.java | 181 ------------------ 4 files changed, 1 insertion(+), 287 deletions(-) delete mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallRoutingController.java delete mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerTest.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 18dec2bf8..85abf1565 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -110,7 +110,6 @@ import org.whispersystems.textsecuregcm.controllers.AccountControllerV2; import org.whispersystems.textsecuregcm.controllers.ArchiveController; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV4; import org.whispersystems.textsecuregcm.controllers.CallLinkController; -import org.whispersystems.textsecuregcm.controllers.CallRoutingController; import org.whispersystems.textsecuregcm.controllers.CallRoutingControllerV2; import org.whispersystems.textsecuregcm.controllers.CertificateController; import org.whispersystems.textsecuregcm.controllers.ChallengeController; @@ -1116,7 +1115,6 @@ public class WhisperServerService extends Application address = Optional.empty(); - try { - final String remoteAddress = (String) requestContext.getProperty( - RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME); - address = Optional.of(InetAddress.getByName(remoteAddress)); - } catch (UnknownHostException e) { - INVALID_IP_COUNTER.increment(); - } - - TurnServerOptions options = turnCallRouter.getRoutingFor(aci, address, TURN_INSTANCE_LIMIT); - return tokenGenerator.generateWithTurnServerOptions(options); - } -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerV2.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerV2.java index f3f325895..de1f46b07 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerV2.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerV2.java @@ -40,7 +40,7 @@ import org.whispersystems.websocket.auth.ReadOnly; public class CallRoutingControllerV2 { private static final Counter INVALID_IP_COUNTER = Metrics.counter(name(CallRoutingControllerV2.class, "invalidIP")); - private static final Counter CLOUDFLARE_TURN_ERROR_COUNTER = Metrics.counter(name(CallRoutingController.class, "cloudflareTurnError")); + private static final Counter CLOUDFLARE_TURN_ERROR_COUNTER = Metrics.counter(name(CallRoutingControllerV2.class, "cloudflareTurnError")); private final RateLimiters rateLimiters; private final TurnCallRouter turnCallRouter; private final TurnTokenGenerator tokenGenerator; diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerTest.java deleted file mode 100644 index bc57929e6..000000000 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CallRoutingControllerTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2013 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.controllers; - -import static org.assertj.core.api.Assertions.assertThat; -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 io.dropwizard.auth.AuthValueFactoryProvider; -import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; -import io.dropwizard.testing.junit5.ResourceExtension; -import jakarta.ws.rs.core.Response; -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -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; -import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice; -import org.whispersystems.textsecuregcm.auth.CloudflareTurnCredentialsManager; -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.experiment.ExperimentEnrollmentManager; -import org.whispersystems.textsecuregcm.limits.RateLimiter; -import org.whispersystems.textsecuregcm.limits.RateLimiters; -import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper; -import org.whispersystems.textsecuregcm.tests.util.AuthHelper; -import org.whispersystems.textsecuregcm.util.SystemMapper; -import org.whispersystems.textsecuregcm.util.TestRemoteAddressFilterProvider; - -@ExtendWith(DropwizardExtensionsSupport.class) -class CallRoutingControllerTest { - - private static final String GET_CALL_ENDPOINTS_PATH = "v1/calling/relays"; - private static final String REMOTE_ADDRESS = "123.123.123.1"; - - private static final RateLimiters rateLimiters = mock(RateLimiters.class); - private static final RateLimiter getCallEndpointLimiter = mock(RateLimiter.class); - private static final ExperimentEnrollmentManager experimentEnrollmentManager = mock( - ExperimentEnrollmentManager.class); - private static final TurnTokenGenerator turnTokenGenerator = - new TurnTokenGenerator("bloop".getBytes(StandardCharsets.UTF_8)); - private static final CloudflareTurnCredentialsManager cloudflareTurnCredentialsManager = mock( - CloudflareTurnCredentialsManager.class); - - private static final TurnCallRouter turnCallRouter = mock(TurnCallRouter.class); - - private static final ResourceExtension resources = ResourceExtension.builder() - .addProvider(AuthHelper.getAuthFilter()) - .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class)) - .addProvider(new RateLimitExceededExceptionMapper()) - .addProvider(new TestRemoteAddressFilterProvider(REMOTE_ADDRESS)) - .setMapper(SystemMapper.jsonMapper()) - .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) - .addResource(new CallRoutingController(rateLimiters, turnCallRouter, turnTokenGenerator, - experimentEnrollmentManager, cloudflareTurnCredentialsManager)) - .build(); - - @BeforeEach - void setup() { - when(rateLimiters.getCallEndpointLimiter()).thenReturn(getCallEndpointLimiter); - } - - @AfterEach - void tearDown() { - reset(experimentEnrollmentManager, rateLimiters, getCallEndpointLimiter, turnCallRouter); - } - - @Test - void testGetTurnEndpointsSuccess() throws UnknownHostException { - TurnServerOptions options = new TurnServerOptions( - "example.domain.org", - Optional.of(List.of("stun:12.34.56.78")), - Optional.of(List.of("stun:example.domain.org")) - ); - - when(turnCallRouter.getRoutingFor( - eq(AuthHelper.VALID_UUID), - eq(Optional.of(InetAddress.getByName(REMOTE_ADDRESS))), - anyInt()) - ).thenReturn(options); - 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()).isEqualTo(options.hostname()); - assertThat(token.urlsWithIps()).isEqualTo(options.urlsWithIps().get()); - assertThat(token.urls()).isEqualTo(options.urlsWithHostname().get()); - } - } - - @Test - void testGetTurnEndpointsCloudflare() throws IOException { - when(experimentEnrollmentManager.isEnrolled(AuthHelper.VALID_NUMBER, AuthHelper.VALID_UUID, "cloudflareTurn")) - .thenReturn(true); - - when(cloudflareTurnCredentialsManager.retrieveFromCloudflare()).thenReturn(new TurnToken("ABC", "XYZ", - List.of("turn:cloudflare.example.com:3478?transport=udp"), Collections.emptyList(), - "cf.example.com")); - - 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()).isEqualTo("ABC"); - assertThat(token.password()).isEqualTo("XYZ"); - assertThat(token.hostname()).isEqualTo("cf.example.com"); - assertThat(token.urlsWithIps()).isEmpty(); - assertThat(token.urls()).isEqualTo(List.of("turn:cloudflare.example.com:3478?transport=udp")); - } - } - - @Test - void testGetTurnEndpointsInvalidIpSuccess() throws UnknownHostException { - TurnServerOptions options = new TurnServerOptions( - "example.domain.org", - Optional.of(List.of()), - Optional.of(List.of("stun:example.domain.org")) - ); - - when(turnCallRouter.getRoutingFor( - eq(AuthHelper.VALID_UUID), - eq(Optional.of(InetAddress.getByName(REMOTE_ADDRESS))), - anyInt()) - ).thenReturn(options); - 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()).isEqualTo(options.hostname()); - assertThat(token.urlsWithIps()).isEqualTo(options.urlsWithIps().get()); - assertThat(token.urls()).isEqualTo(options.urlsWithHostname().get()); - } - } - - @Test - void testGetTurnEndpointRateLimited() throws RateLimitExceededException { - doThrow(new RateLimitExceededException(null)) - .when(getCallEndpointLimiter).validate(AuthHelper.VALID_UUID); - - try (final 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(429); - } - } -}