mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-19 22:28:04 +01:00
Move score floor to dynamic configuration, add distribution summary
This commit is contained in:
@@ -476,11 +476,12 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration());
|
||||
LegacyRecaptchaClient legacyRecaptchaClient = new LegacyRecaptchaClient(config.getRecaptchaConfiguration().getSecret());
|
||||
EnterpriseRecaptchaClient enterpriseRecaptchaClient = new EnterpriseRecaptchaClient(
|
||||
config.getRecaptchaV2Configuration().getScoreFloor().doubleValue(),
|
||||
config.getRecaptchaV2Configuration().getProjectPath(),
|
||||
config.getRecaptchaV2Configuration().getCredentialConfigurationJson());
|
||||
TransitionalRecaptchaClient transitionalRecaptchaClient = new TransitionalRecaptchaClient(legacyRecaptchaClient, enterpriseRecaptchaClient);
|
||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
||||
config.getRecaptchaV2Configuration().getCredentialConfigurationJson(),
|
||||
dynamicConfigurationManager);
|
||||
TransitionalRecaptchaClient transitionalRecaptchaClient = new TransitionalRecaptchaClient(legacyRecaptchaClient,
|
||||
enterpriseRecaptchaClient);
|
||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
||||
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
||||
transitionalRecaptchaClient, dynamicRateLimiters);
|
||||
RateLimitChallengeOptionManager rateLimitChallengeOptionManager =
|
||||
|
||||
@@ -5,25 +5,13 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class RecaptchaV2Configuration {
|
||||
|
||||
private BigDecimal scoreFloor;
|
||||
private String projectPath;
|
||||
private String credentialConfigurationJson;
|
||||
|
||||
@DecimalMin("0")
|
||||
@DecimalMax("1")
|
||||
@NotNull
|
||||
public BigDecimal getScoreFloor() {
|
||||
return scoreFloor;
|
||||
}
|
||||
|
||||
@NotEmpty
|
||||
public String getProjectPath() {
|
||||
return projectPath;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class DynamicCaptchaConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@DecimalMin("0")
|
||||
@DecimalMax("1")
|
||||
@NotNull
|
||||
private BigDecimal scoreFloor;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Set<String> signupCountryCodes = Collections.emptySet();
|
||||
|
||||
public Set<String> getSignupCountryCodes() {
|
||||
return signupCountryCodes;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSignupCountryCodes(Set<String> numbers) {
|
||||
this.signupCountryCodes = numbers;
|
||||
}
|
||||
|
||||
public BigDecimal getScoreFloor() {
|
||||
return scoreFloor;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@ public class DynamicConfiguration {
|
||||
private DynamicTwilioConfiguration twilio = new DynamicTwilioConfiguration();
|
||||
|
||||
@JsonProperty
|
||||
private DynamicSignupCaptchaConfiguration signupCaptcha = new DynamicSignupCaptchaConfiguration();
|
||||
@Valid
|
||||
private DynamicCaptchaConfiguration captcha = new DynamicCaptchaConfiguration();
|
||||
|
||||
@JsonProperty
|
||||
@Valid
|
||||
@@ -86,8 +87,8 @@ public class DynamicConfiguration {
|
||||
this.twilio = twilioConfiguration;
|
||||
}
|
||||
|
||||
public DynamicSignupCaptchaConfiguration getSignupCaptchaConfiguration() {
|
||||
return signupCaptcha;
|
||||
public DynamicCaptchaConfiguration getCaptchaConfiguration() {
|
||||
return captcha;
|
||||
}
|
||||
|
||||
public DynamicRateLimitChallengeConfiguration getRateLimitChallengeConfiguration() {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class DynamicSignupCaptchaConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Set<String> countryCodes = Collections.emptySet();
|
||||
|
||||
public Set<String> getCountryCodes() {
|
||||
return countryCodes;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setCountryCodes(Set<String> numbers) {
|
||||
this.countryCodes = numbers;
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,8 @@ import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicSignupCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
|
||||
@@ -770,8 +770,9 @@ public class AccountController {
|
||||
return new CaptchaRequirement(true, true);
|
||||
}
|
||||
|
||||
DynamicSignupCaptchaConfiguration signupCaptchaConfig = dynamicConfigurationManager.getConfiguration().getSignupCaptchaConfiguration();
|
||||
if (signupCaptchaConfig.getCountryCodes().contains(countryCode)) {
|
||||
DynamicCaptchaConfiguration captchaConfig = dynamicConfigurationManager.getConfiguration()
|
||||
.getCaptchaConfiguration();
|
||||
if (captchaConfig.getSignupCountryCodes().contains(countryCode)) {
|
||||
return new CaptchaRequirement(true, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.recaptcha;
|
||||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
|
||||
@@ -12,36 +14,38 @@ import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceSetting
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.recaptchaenterprise.v1.Assessment;
|
||||
import com.google.recaptchaenterprise.v1.Event;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
public class EnterpriseRecaptchaClient implements RecaptchaClient {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String SEPARATOR = ".";
|
||||
private static final Logger logger = LoggerFactory.getLogger(EnterpriseRecaptchaClient.class);
|
||||
private static final String SCORE_DISTRIBUTION_NAME = name(EnterpriseRecaptchaClient.class, "scoreDistribution");
|
||||
|
||||
private final double scoreFloor;
|
||||
private final String projectPath;
|
||||
private final RecaptchaEnterpriseServiceClient client;
|
||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||
|
||||
public EnterpriseRecaptchaClient(
|
||||
final double scoreFloor,
|
||||
@Nonnull final String projectPath,
|
||||
@Nonnull final String recaptchaCredentialConfigurationJson) {
|
||||
@Nonnull final String recaptchaCredentialConfigurationJson,
|
||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||
try {
|
||||
this.scoreFloor = scoreFloor;
|
||||
this.projectPath = Objects.requireNonNull(projectPath);
|
||||
this.client = RecaptchaEnterpriseServiceClient.create(RecaptchaEnterpriseServiceSettings.newBuilder()
|
||||
.setCredentialsProvider(FixedCredentialsProvider.create(GoogleCredentials.fromStream(
|
||||
new ByteArrayInputStream(recaptchaCredentialConfigurationJson.getBytes(StandardCharsets.UTF_8)))))
|
||||
.build());
|
||||
|
||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@@ -89,6 +93,15 @@ public class EnterpriseRecaptchaClient implements RecaptchaClient {
|
||||
final Event event = eventBuilder.build();
|
||||
final Assessment assessment = client.createAssessment(projectPath, Assessment.newBuilder().setEvent(event).build());
|
||||
|
||||
return assessment.getTokenProperties().getValid() && assessment.getRiskAnalysis().getScore() >= scoreFloor;
|
||||
if (assessment.getTokenProperties().getValid()) {
|
||||
final float score = assessment.getRiskAnalysis().getScore();
|
||||
Metrics.summary(SCORE_DISTRIBUTION_NAME).record(score);
|
||||
|
||||
return score >= dynamicConfigurationManager.getConfiguration().getCaptchaConfiguration().getScoreFloor()
|
||||
.floatValue();
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user