Add by-action captcha score config

Enable setting different captcha score thresholds for different captcha
actions via configuration
This commit is contained in:
Ravi Khadiwala 2023-02-28 10:21:59 -06:00 committed by ravi-signal
parent 437bc1358b
commit 59bc2c5535
4 changed files with 22 additions and 2 deletions

View File

@ -9,6 +9,7 @@ import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Metrics;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.http.HttpClient; import java.net.http.HttpClient;
@ -109,6 +110,8 @@ public class HCaptchaClient implements CaptchaClient {
"reason", reason, "reason", reason,
"score", scoreString).increment(); "score", scoreString).increment();
} }
return new AssessmentResult(score >= config.getScoreFloor().floatValue(), scoreString);
final BigDecimal threshold = config.getScoreFloorByAction().getOrDefault(action, config.getScoreFloor());
return new AssessmentResult(score >= threshold.floatValue(), scoreString);
} }
} }

View File

@ -18,6 +18,7 @@ import com.google.recaptchaenterprise.v1.RiskAnalysis;
import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Metrics;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -99,8 +100,9 @@ public class RecaptchaClient implements CaptchaClient {
"reason", reason.name()) "reason", reason.name())
.increment(); .increment();
} }
final BigDecimal threshold = config.getScoreFloorByAction().getOrDefault(expectedAction, config.getScoreFloor());
return new AssessmentResult( return new AssessmentResult(
score >= config.getScoreFloor().floatValue(), score >= threshold.floatValue(),
AssessmentResult.scoreString(score)); AssessmentResult.scoreString(score));
} else { } else {
Metrics.counter(INVALID_REASON_COUNTER_NAME, Metrics.counter(INVALID_REASON_COUNTER_NAME,

View File

@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin; import javax.validation.constraints.DecimalMin;
@ -28,6 +29,11 @@ public class DynamicCaptchaConfiguration {
@JsonProperty @JsonProperty
private boolean allowRecaptcha = true; private boolean allowRecaptcha = true;
@JsonProperty
@NotNull
private Map<String, BigDecimal> scoreFloorByAction = Collections.emptyMap();
@JsonProperty @JsonProperty
@NotNull @NotNull
private Set<String> signupCountryCodes = Collections.emptySet(); private Set<String> signupCountryCodes = Collections.emptySet();
@ -66,6 +72,10 @@ public class DynamicCaptchaConfiguration {
return allowRecaptcha; return allowRecaptcha;
} }
public Map<String, BigDecimal> getScoreFloorByAction() {
return scoreFloorByAction;
}
@VisibleForTesting @VisibleForTesting
public void setAllowHCaptcha(final boolean allowHCaptcha) { public void setAllowHCaptcha(final boolean allowHCaptcha) {
this.allowHCaptcha = allowHCaptcha; this.allowHCaptcha = allowHCaptcha;

View File

@ -262,6 +262,9 @@ class DynamicConfigurationTest {
signupCountryCodes: signupCountryCodes:
- 1 - 1
scoreFloor: 0.9 scoreFloor: 0.9
scoreFloorByAction:
challenge: 0.1
registration: 0.2
"""; """;
final DynamicCaptchaConfiguration config = final DynamicCaptchaConfiguration config =
@ -270,6 +273,8 @@ class DynamicConfigurationTest {
assertEquals(Set.of("1"), config.getSignupCountryCodes()); assertEquals(Set.of("1"), config.getSignupCountryCodes());
assertEquals(0.9f, config.getScoreFloor().floatValue()); assertEquals(0.9f, config.getScoreFloor().floatValue());
assertEquals(0.1f, config.getScoreFloorByAction().get("challenge").floatValue());
assertEquals(0.2f, config.getScoreFloorByAction().get("registration").floatValue());
} }
} }