Add a captcha short-code expander

This commit is contained in:
Ravi Khadiwala
2023-07-07 10:55:50 -05:00
committed by ravi-signal
parent 1dde612855
commit 3ac7aba6b2
7 changed files with 110 additions and 9 deletions

View File

@@ -29,9 +29,15 @@ public class CaptchaChecker {
@VisibleForTesting
static final String SEPARATOR = ".";
private static final String SHORT_SUFFIX = "-short";
private final ShortCodeExpander shortCodeExpander;
private final Map<String, CaptchaClient> captchaClientMap;
public CaptchaChecker(final List<CaptchaClient> captchaClients) {
public CaptchaChecker(
final ShortCodeExpander shortCodeRetriever,
final List<CaptchaClient> captchaClients) {
this.shortCodeExpander = shortCodeRetriever;
this.captchaClientMap = captchaClients.stream()
.collect(Collectors.toMap(CaptchaClient::scheme, Function.identity()));
}
@@ -63,9 +69,17 @@ public class CaptchaChecker {
final String prefix = parts[0];
final String siteKey = parts[1].toLowerCase(Locale.ROOT).strip();
final String action = parts[2];
final String token = parts[3];
String token = parts[3];
final CaptchaClient client = this.captchaClientMap.get(prefix);
String provider = prefix;
if (prefix.endsWith(SHORT_SUFFIX)) {
// This is a "short" solution that points to the actual solution. We need to fetch the
// full solution before proceeding
provider = prefix.substring(0, prefix.length() - SHORT_SUFFIX.length());
token = shortCodeExpander.retrieve(token).orElseThrow(() -> new BadRequestException("invalid shortcode"));
}
final CaptchaClient client = this.captchaClientMap.get(provider);
if (client == null) {
throw new BadRequestException("invalid captcha scheme");
}
@@ -92,7 +106,7 @@ public class CaptchaChecker {
Metrics.counter(ASSESSMENTS_COUNTER_NAME,
"action", action,
"score", result.getScoreString(),
"provider", prefix)
"provider", provider)
.increment();
return result;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.captcha;
import io.micrometer.core.instrument.Metrics;
import org.apache.http.HttpStatus;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Optional;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
public class ShortCodeExpander {
private static final String EXPAND_COUNTER_NAME = name(ShortCodeExpander.class, "expand");
private final HttpClient client;
private final URI shortenerHost;
public ShortCodeExpander(final HttpClient client, final String shortenerHost) {
this.client = client;
this.shortenerHost = URI.create(shortenerHost);
}
public Optional<String> retrieve(final String shortCode) throws IOException {
final URI uri = shortenerHost.resolve("/" + shortCode);
final HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
try {
final HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
Metrics.counter(EXPAND_COUNTER_NAME, "responseCode", Integer.toString(response.statusCode())).increment();
return switch (response.statusCode()) {
case HttpStatus.SC_OK -> Optional.of(response.body());
case HttpStatus.SC_NOT_FOUND -> Optional.empty();
default -> throw new IOException("Failed to look up shortcode");
};
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}