Add HttpServletRequestUtil
This commit is contained in:
parent
fb39af67e5
commit
c838df90ef
|
@ -43,6 +43,7 @@ import org.whispersystems.textsecuregcm.spam.FilterSpam;
|
||||||
import org.whispersystems.textsecuregcm.spam.PushChallengeConfig;
|
import org.whispersystems.textsecuregcm.spam.PushChallengeConfig;
|
||||||
import org.whispersystems.textsecuregcm.spam.ScoreThreshold;
|
import org.whispersystems.textsecuregcm.spam.ScoreThreshold;
|
||||||
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
||||||
|
import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
||||||
|
|
||||||
@Path("/v1/challenge")
|
@Path("/v1/challenge")
|
||||||
@Tag(name = "Challenge")
|
@Tag(name = "Challenge")
|
||||||
|
@ -103,7 +104,7 @@ public class ChallengeController {
|
||||||
tags = tags.and(CHALLENGE_TYPE_TAG, "recaptcha");
|
tags = tags.and(CHALLENGE_TYPE_TAG, "recaptcha");
|
||||||
|
|
||||||
final String remoteAddress = useRemoteAddress
|
final String remoteAddress = useRemoteAddress
|
||||||
? request.getRemoteAddr()
|
? HttpServletRequestUtil.getRemoteAddress(request)
|
||||||
: HeaderUtils.getMostRecentProxy(forwardedFor).orElseThrow(BadRequestException::new);
|
: HeaderUtils.getMostRecentProxy(forwardedFor).orElseThrow(BadRequestException::new);
|
||||||
boolean success = rateLimitChallengeManager.answerRecaptchaChallenge(
|
boolean success = rateLimitChallengeManager.answerRecaptchaChallenge(
|
||||||
auth.getAccount(),
|
auth.getAccount(),
|
||||||
|
|
|
@ -89,6 +89,7 @@ import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsMan
|
||||||
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
||||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||||
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
||||||
|
import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
||||||
import org.whispersystems.textsecuregcm.util.Pair;
|
import org.whispersystems.textsecuregcm.util.Pair;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
|
@ -212,7 +213,7 @@ public class VerificationController {
|
||||||
@NotNull @Extract final SenderOverride senderOverride) {
|
@NotNull @Extract final SenderOverride senderOverride) {
|
||||||
|
|
||||||
final String sourceHost = useRemoteAddress
|
final String sourceHost = useRemoteAddress
|
||||||
? request.getRemoteAddr()
|
? HttpServletRequestUtil.getRemoteAddress(request)
|
||||||
: HeaderUtils.getMostRecentProxy(forwardedFor).orElseThrow();
|
: HeaderUtils.getMostRecentProxy(forwardedFor).orElseThrow();
|
||||||
|
|
||||||
final Pair<String, PushNotification.TokenType> pushTokenAndType = validateAndExtractPushToken(
|
final Pair<String, PushNotification.TokenType> pushTokenAndType = validateAndExtractPushToken(
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
|
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
import org.whispersystems.textsecuregcm.util.HeaderUtils;
|
||||||
|
import org.whispersystems.textsecuregcm.util.HttpServletRequestUtil;
|
||||||
|
|
||||||
public class RateLimitByIpFilter implements ContainerRequestFilter {
|
public class RateLimitByIpFilter implements ContainerRequestFilter {
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ public class RateLimitByIpFilter implements ContainerRequestFilter {
|
||||||
try {
|
try {
|
||||||
final String xffHeader = requestContext.getHeaders().getFirst(HttpHeaders.X_FORWARDED_FOR);
|
final String xffHeader = requestContext.getHeaders().getFirst(HttpHeaders.X_FORWARDED_FOR);
|
||||||
final Optional<String> remoteAddress = useRemoteAddress
|
final Optional<String> remoteAddress = useRemoteAddress
|
||||||
? Optional.of(httpServletRequestProvider.get().getRemoteAddr())
|
? Optional.of(HttpServletRequestUtil.getRemoteAddress(httpServletRequestProvider.get()))
|
||||||
: Optional.ofNullable(xffHeader)
|
: Optional.ofNullable(xffHeader)
|
||||||
.flatMap(HeaderUtils::getMostRecentProxy);
|
.flatMap(HeaderUtils::getMostRecentProxy);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.util;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public class HttpServletRequestUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote address of the request, removing bracket ("[…]") host notation from IPv6 addresses present in
|
||||||
|
* some implementations, notably {@link org.eclipse.jetty.server.HttpChannel}.
|
||||||
|
*/
|
||||||
|
public static String getRemoteAddress(final HttpServletRequest request) {
|
||||||
|
final String remoteAddr = request.getRemoteAddr();
|
||||||
|
|
||||||
|
if (remoteAddr.startsWith("[")) {
|
||||||
|
return remoteAddr.substring(1, remoteAddr.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteAddr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.util;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
|
import io.dropwizard.core.Application;
|
||||||
|
import io.dropwizard.core.Configuration;
|
||||||
|
import io.dropwizard.core.setup.Environment;
|
||||||
|
import io.dropwizard.testing.junit5.DropwizardAppExtension;
|
||||||
|
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import org.eclipse.jetty.util.HostPort;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
|
class HttpServletRequestUtilIntegrationTest {
|
||||||
|
|
||||||
|
private static final String PATH = "/test";
|
||||||
|
|
||||||
|
// The Grizzly test container does not match the Jetty container used in real deployments, and JettyTestContainerFactory
|
||||||
|
// in jersey-test-framework-provider-jetty doesn’t easily support @Context HttpServletRequest, so this test runs a
|
||||||
|
// full Jetty server in a separate process
|
||||||
|
private final DropwizardAppExtension<TestConfiguration> EXTENSION = new DropwizardAppExtension<>(
|
||||||
|
TestApplication.class);
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {"127.0.0.1", "0:0:0:0:0:0:0:1"})
|
||||||
|
void test(String ip) throws Exception {
|
||||||
|
final Set<String> addresses = Arrays.stream(InetAddress.getAllByName("localhost"))
|
||||||
|
.map(InetAddress::getHostAddress)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
assumeTrue(addresses.contains(ip), String.format("localhost does not resolve to %s", ip));
|
||||||
|
|
||||||
|
Client client = EXTENSION.client();
|
||||||
|
|
||||||
|
final TestResponse response = client.target(
|
||||||
|
String.format("http://%s:%d%s", HostPort.normalizeHost(ip), EXTENSION.getLocalPort(), PATH))
|
||||||
|
.request("application/json")
|
||||||
|
.get(TestResponse.class);
|
||||||
|
|
||||||
|
assertEquals(ip, response.remoteAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path(PATH)
|
||||||
|
public static class TestController {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public TestResponse get(@Context HttpServletRequest request) {
|
||||||
|
|
||||||
|
return new TestResponse(HttpServletRequestUtil.getRemoteAddress(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record TestResponse(String remoteAddress) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestApplication extends Application<TestConfiguration> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final TestConfiguration configuration, final Environment environment) throws Exception {
|
||||||
|
environment.jersey().register(new TestController());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestConfiguration extends Configuration {}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.util;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
|
class HttpServletRequestUtilTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"127.0.0.1, 127.0.0.1",
|
||||||
|
"[0:0:0:0:0:0:0:1], 0:0:0:0:0:0:0:1"
|
||||||
|
})
|
||||||
|
void testGetRemoteAddress(final String remoteAddr, final String expectedRemoteAddr) {
|
||||||
|
final HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
|
||||||
|
when(httpServletRequest.getRemoteAddr()).thenReturn(remoteAddr);
|
||||||
|
|
||||||
|
assertEquals(expectedRemoteAddr, HttpServletRequestUtil.getRemoteAddress(httpServletRequest));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue