Support different v2 captcha actions

This commit is contained in:
Chris Eager
2022-02-23 15:23:37 -08:00
committed by Chris Eager
parent 7ded802df4
commit f3457502a6
3 changed files with 120 additions and 9 deletions

View File

@@ -11,13 +11,14 @@ import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceSettings;
import com.google.recaptchaenterprise.v1.Assessment;
import com.google.recaptchaenterprise.v1.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EnterpriseRecaptchaClient implements RecaptchaClient {
private static final Logger logger = LoggerFactory.getLogger(EnterpriseRecaptchaClient.class);
@@ -47,12 +48,20 @@ public class EnterpriseRecaptchaClient implements RecaptchaClient {
@Override
public boolean verify(final String token, final String ip) {
Event event = Event.newBuilder()
.setExpectedAction("challenge")
return verify(token, ip, null);
}
public boolean verify(final String token, final String ip, @Nullable final String expectedAction) {
Event.Builder eventBuilder = Event.newBuilder()
.setSiteKey(siteKey)
.setToken(token)
.setUserIpAddress(ip)
.build();
.setUserIpAddress(ip);
if (expectedAction != null) {
eventBuilder.setExpectedAction(expectedAction);
}
final Event event = eventBuilder.build();
final Assessment assessment = client.createAssessment(projectPath, Assessment.newBuilder().setEvent(event).build());
return assessment.getTokenProperties().getValid() && assessment.getRiskAnalysis().getScore() >= scoreFloor;

View File

@@ -5,12 +5,16 @@
package org.whispersystems.textsecuregcm.recaptcha;
import com.google.common.annotations.VisibleForTesting;
import java.util.Objects;
import javax.annotation.Nonnull;
public class TransitionalRecaptchaClient implements RecaptchaClient {
private static final String V2_PREFIX = "signal-recaptcha-v2:";
@VisibleForTesting
static final String SEPARATOR = ".";
@VisibleForTesting
static final String V2_PREFIX = "signal-recaptcha-v2" + SEPARATOR;
private final LegacyRecaptchaClient legacyRecaptchaClient;
private final EnterpriseRecaptchaClient enterpriseRecaptchaClient;
@@ -25,9 +29,28 @@ public class TransitionalRecaptchaClient implements RecaptchaClient {
@Override
public boolean verify(@Nonnull final String token, @Nonnull final String ip) {
if (token.startsWith(V2_PREFIX)) {
return enterpriseRecaptchaClient.verify(token.substring(V2_PREFIX.length()), ip);
final String[] actionAndToken = parseV2ActionAndToken(token.substring(V2_PREFIX.length()));
return enterpriseRecaptchaClient.verify(actionAndToken[1], ip, actionAndToken[0]);
} else {
return legacyRecaptchaClient.verify(token, ip);
}
}
/**
* Parses the token and action (if any) from {@code input}. The expected input format is: {@code [action:]token}.
* <p>
* For action to be optional, there is a strong assumption that the token will never contain a {@value SEPARATOR}.
* Observation suggests {@code token} is base-64 encoded. In practice, an action should always be present, but we
* dont need to be strict.
*/
static String[] parseV2ActionAndToken(final String input) {
String[] actionAndToken = input.split("\\" + SEPARATOR, 2);
if (actionAndToken.length == 1) {
// there was no ":" delimiter; assume we only have a token
return new String[]{null, actionAndToken[0]};
}
return actionAndToken;
}
}