Make the enterprise client canonical
This commit is contained in:
parent
2a68d9095d
commit
1395dcc0be
|
@ -242,7 +242,7 @@ voiceVerification:
|
||||||
locales:
|
locales:
|
||||||
- en
|
- en
|
||||||
|
|
||||||
recaptchaV2:
|
recaptcha:
|
||||||
projectPath: projects/example
|
projectPath: projects/example
|
||||||
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
credentialConfigurationJson: "{ }" # service account configuration for backend authentication
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RecaptchaV2Configuration;
|
import org.whispersystems.textsecuregcm.configuration.RecaptchaConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
||||||
|
@ -208,7 +208,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RecaptchaV2Configuration recaptchaV2;
|
private RecaptchaConfiguration recaptcha;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -283,8 +283,8 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
return dynamoDbTables;
|
return dynamoDbTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecaptchaV2Configuration getRecaptchaV2Configuration() {
|
public RecaptchaConfiguration getRecaptchaConfiguration() {
|
||||||
return recaptchaV2;
|
return recaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VoiceVerificationConfiguration getVoiceVerificationConfiguration() {
|
public VoiceVerificationConfiguration getVoiceVerificationConfiguration() {
|
||||||
|
|
|
@ -150,7 +150,7 @@ import org.whispersystems.textsecuregcm.push.GCMSender;
|
||||||
import org.whispersystems.textsecuregcm.push.MessageSender;
|
import org.whispersystems.textsecuregcm.push.MessageSender;
|
||||||
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
|
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
|
||||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||||
import org.whispersystems.textsecuregcm.recaptcha.EnterpriseRecaptchaClient;
|
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.redis.ConnectionEventLogger;
|
import org.whispersystems.textsecuregcm.redis.ConnectionEventLogger;
|
||||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||||
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
|
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
|
||||||
|
@ -472,13 +472,13 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
MessageSender messageSender = new MessageSender(apnFallbackManager, clientPresenceManager, messagesManager, gcmSender, apnSender, pushLatencyManager);
|
MessageSender messageSender = new MessageSender(apnFallbackManager, clientPresenceManager, messagesManager, gcmSender, apnSender, pushLatencyManager);
|
||||||
ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender);
|
ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender);
|
||||||
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration());
|
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(config.getTurnConfiguration());
|
||||||
EnterpriseRecaptchaClient enterpriseRecaptchaClient = new EnterpriseRecaptchaClient(
|
RecaptchaClient recaptchaClient = new RecaptchaClient(
|
||||||
config.getRecaptchaV2Configuration().getProjectPath(),
|
config.getRecaptchaConfiguration().getProjectPath(),
|
||||||
config.getRecaptchaV2Configuration().getCredentialConfigurationJson(),
|
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
|
||||||
dynamicConfigurationManager);
|
dynamicConfigurationManager);
|
||||||
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
|
||||||
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
|
||||||
enterpriseRecaptchaClient, dynamicRateLimiters);
|
recaptchaClient, dynamicRateLimiters);
|
||||||
RateLimitChallengeOptionManager rateLimitChallengeOptionManager =
|
RateLimitChallengeOptionManager rateLimitChallengeOptionManager =
|
||||||
new RateLimitChallengeOptionManager(dynamicRateLimiters, dynamicConfigurationManager);
|
new RateLimitChallengeOptionManager(dynamicRateLimiters, dynamicConfigurationManager);
|
||||||
|
|
||||||
|
@ -615,7 +615,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
environment.jersey().register(
|
environment.jersey().register(
|
||||||
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
|
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
|
||||||
smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
|
smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
|
||||||
enterpriseRecaptchaClient, gcmSender, apnSender, backupCredentialsGenerator,
|
recaptchaClient, gcmSender, apnSender, backupCredentialsGenerator,
|
||||||
verifyExperimentEnrollmentManager));
|
verifyExperimentEnrollmentManager));
|
||||||
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager));
|
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager));
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
public class RecaptchaV2Configuration {
|
public class RecaptchaConfiguration {
|
||||||
|
|
||||||
private String projectPath;
|
private String projectPath;
|
||||||
private String credentialConfigurationJson;
|
private String credentialConfigurationJson;
|
|
@ -1,125 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021-2022 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
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;
|
|
||||||
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceSettings;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.recaptchaenterprise.v1.Assessment;
|
|
||||||
import com.google.recaptchaenterprise.v1.Event;
|
|
||||||
import io.micrometer.core.instrument.DistributionSummary;
|
|
||||||
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.apache.commons.lang3.StringUtils;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
|
||||||
|
|
||||||
public class EnterpriseRecaptchaClient implements RecaptchaClient {
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String SEPARATOR = ".";
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String V2_PREFIX = "signal-recaptcha-v2" + EnterpriseRecaptchaClient.SEPARATOR;
|
|
||||||
private static final String ASSESSMENTS_COUNTER_NAME = name(EnterpriseRecaptchaClient.class, "assessments");
|
|
||||||
private static final String SCORE_DISTRIBUTION_NAME = name(EnterpriseRecaptchaClient.class, "scoreDistribution");
|
|
||||||
|
|
||||||
private final String projectPath;
|
|
||||||
private final RecaptchaEnterpriseServiceClient client;
|
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
|
||||||
|
|
||||||
public EnterpriseRecaptchaClient(
|
|
||||||
@Nonnull final String projectPath,
|
|
||||||
@Nonnull final String recaptchaCredentialConfigurationJson,
|
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* don’t need to be strict.
|
|
||||||
*/
|
|
||||||
static String[] parseInputToken(final String input) {
|
|
||||||
String[] keyActionAndToken = StringUtils.removeStart(input, V2_PREFIX).split("\\" + SEPARATOR, 3);
|
|
||||||
|
|
||||||
if (keyActionAndToken.length == 1) {
|
|
||||||
throw new BadRequestException("too few parts");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyActionAndToken.length == 2) {
|
|
||||||
// there was no ":" delimiter; assume we only have a token
|
|
||||||
return new String[]{keyActionAndToken[0], null, keyActionAndToken[1]};
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyActionAndToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean verify(final String input, final String ip) {
|
|
||||||
final String[] parts = parseInputToken(input);
|
|
||||||
|
|
||||||
final String sitekey = parts[0];
|
|
||||||
final String expectedAction = parts[1];
|
|
||||||
final String token = parts[2];
|
|
||||||
|
|
||||||
Event.Builder eventBuilder = Event.newBuilder()
|
|
||||||
.setSiteKey(sitekey)
|
|
||||||
.setToken(token)
|
|
||||||
.setUserIpAddress(ip);
|
|
||||||
|
|
||||||
if (expectedAction != null) {
|
|
||||||
eventBuilder.setExpectedAction(expectedAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Event event = eventBuilder.build();
|
|
||||||
final Assessment assessment = client.createAssessment(projectPath, Assessment.newBuilder().setEvent(event).build());
|
|
||||||
|
|
||||||
Metrics.counter(ASSESSMENTS_COUNTER_NAME,
|
|
||||||
"action", String.valueOf(expectedAction),
|
|
||||||
"valid", String.valueOf(assessment.getTokenProperties().getValid()))
|
|
||||||
.increment();
|
|
||||||
|
|
||||||
if (assessment.getTokenProperties().getValid()) {
|
|
||||||
final float score = assessment.getRiskAnalysis().getScore();
|
|
||||||
|
|
||||||
final DistributionSummary.Builder distributionSummaryBuilder = DistributionSummary.builder(
|
|
||||||
SCORE_DISTRIBUTION_NAME)
|
|
||||||
// score is 0.0…1.0, which doesn’t play well with distribution summary bucketing, so scale to 0…100
|
|
||||||
.scale(100)
|
|
||||||
.maximumExpectedValue(100.0d)
|
|
||||||
.tags("action", String.valueOf(expectedAction));
|
|
||||||
|
|
||||||
distributionSummaryBuilder.register(Metrics.globalRegistry).record(score);
|
|
||||||
|
|
||||||
return score >= dynamicConfigurationManager.getConfiguration().getCaptchaConfiguration().getScoreFloor()
|
|
||||||
.floatValue();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,125 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 Signal Messenger, LLC
|
* Copyright 2021-2022 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.recaptcha;
|
package org.whispersystems.textsecuregcm.recaptcha;
|
||||||
|
|
||||||
public interface RecaptchaClient {
|
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||||
boolean verify(String token, String ip);
|
|
||||||
|
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||||
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
|
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
|
||||||
|
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceSettings;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.recaptchaenterprise.v1.Assessment;
|
||||||
|
import com.google.recaptchaenterprise.v1.Event;
|
||||||
|
import io.micrometer.core.instrument.DistributionSummary;
|
||||||
|
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.apache.commons.lang3.StringUtils;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
|
||||||
|
public class RecaptchaClient {
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String SEPARATOR = ".";
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String V2_PREFIX = "signal-recaptcha-v2" + RecaptchaClient.SEPARATOR;
|
||||||
|
private static final String ASSESSMENTS_COUNTER_NAME = name(RecaptchaClient.class, "assessments");
|
||||||
|
private static final String SCORE_DISTRIBUTION_NAME = name(RecaptchaClient.class, "scoreDistribution");
|
||||||
|
|
||||||
|
private final String projectPath;
|
||||||
|
private final RecaptchaEnterpriseServiceClient client;
|
||||||
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
|
|
||||||
|
public RecaptchaClient(
|
||||||
|
@Nonnull final String projectPath,
|
||||||
|
@Nonnull final String recaptchaCredentialConfigurationJson,
|
||||||
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the sitekey, token, and action (if any) from {@code input}. The expected input format is: {@code [version
|
||||||
|
* prefix.]sitekey.[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
|
||||||
|
* don’t need to be strict.
|
||||||
|
*/
|
||||||
|
static String[] parseInputToken(final String input) {
|
||||||
|
String[] parts = StringUtils.removeStart(input, V2_PREFIX).split("\\" + SEPARATOR, 3);
|
||||||
|
|
||||||
|
if (parts.length == 1) {
|
||||||
|
throw new BadRequestException("too few parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.length == 2) {
|
||||||
|
// we got some parts, assume it is action that is missing
|
||||||
|
return new String[]{parts[0], null, parts[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(final String input, final String ip) {
|
||||||
|
final String[] parts = parseInputToken(input);
|
||||||
|
|
||||||
|
final String sitekey = parts[0];
|
||||||
|
final String expectedAction = parts[1];
|
||||||
|
final String token = parts[2];
|
||||||
|
|
||||||
|
Event.Builder eventBuilder = Event.newBuilder()
|
||||||
|
.setSiteKey(sitekey)
|
||||||
|
.setToken(token)
|
||||||
|
.setUserIpAddress(ip);
|
||||||
|
|
||||||
|
if (expectedAction != null) {
|
||||||
|
eventBuilder.setExpectedAction(expectedAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Event event = eventBuilder.build();
|
||||||
|
final Assessment assessment = client.createAssessment(projectPath, Assessment.newBuilder().setEvent(event).build());
|
||||||
|
|
||||||
|
Metrics.counter(ASSESSMENTS_COUNTER_NAME,
|
||||||
|
"action", String.valueOf(expectedAction),
|
||||||
|
"valid", String.valueOf(assessment.getTokenProperties().getValid()))
|
||||||
|
.increment();
|
||||||
|
|
||||||
|
if (assessment.getTokenProperties().getValid()) {
|
||||||
|
final float score = assessment.getRiskAnalysis().getScore();
|
||||||
|
|
||||||
|
final DistributionSummary.Builder distributionSummaryBuilder = DistributionSummary.builder(
|
||||||
|
SCORE_DISTRIBUTION_NAME)
|
||||||
|
// score is 0.0…1.0, which doesn’t play well with distribution summary bucketing, so scale to 0…100
|
||||||
|
.scale(100)
|
||||||
|
.maximumExpectedValue(100.0d)
|
||||||
|
.tags("action", String.valueOf(expectedAction));
|
||||||
|
|
||||||
|
distributionSummaryBuilder.register(Metrics.globalRegistry).record(score);
|
||||||
|
|
||||||
|
return score >= dynamicConfigurationManager.getConfiguration().getCaptchaConfiguration().getScoreFloor()
|
||||||
|
.floatValue();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ package org.whispersystems.textsecuregcm.recaptcha;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.whispersystems.textsecuregcm.recaptcha.EnterpriseRecaptchaClient.SEPARATOR;
|
import static org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient.SEPARATOR;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -17,10 +17,10 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
class EnterpriseRecaptchaClientTest {
|
class RecaptchaClientTest {
|
||||||
|
|
||||||
private static final String PREFIX = EnterpriseRecaptchaClient.V2_PREFIX.substring(0,
|
private static final String PREFIX = RecaptchaClient.V2_PREFIX.substring(0,
|
||||||
EnterpriseRecaptchaClient.V2_PREFIX.lastIndexOf(SEPARATOR));
|
RecaptchaClient.V2_PREFIX.lastIndexOf(SEPARATOR));
|
||||||
private static final String SITE_KEY = "site-key";
|
private static final String SITE_KEY = "site-key";
|
||||||
private static final String TOKEN = "some-token";
|
private static final String TOKEN = "some-token";
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class EnterpriseRecaptchaClientTest {
|
||||||
void parseInputToken(final String input, final String expectedToken, final String siteKey,
|
void parseInputToken(final String input, final String expectedToken, final String siteKey,
|
||||||
@Nullable final String expectedAction) {
|
@Nullable final String expectedAction) {
|
||||||
|
|
||||||
final String[] parts = EnterpriseRecaptchaClient.parseInputToken(input);
|
final String[] parts = RecaptchaClient.parseInputToken(input);
|
||||||
|
|
||||||
assertEquals(siteKey, parts[0]);
|
assertEquals(siteKey, parts[0]);
|
||||||
assertEquals(expectedAction, parts[1]);
|
assertEquals(expectedAction, parts[1]);
|
||||||
|
@ -39,7 +39,7 @@ class EnterpriseRecaptchaClientTest {
|
||||||
@Test
|
@Test
|
||||||
void parseInputTokenBadRequest() {
|
void parseInputTokenBadRequest() {
|
||||||
assertThrows(BadRequestException.class, () -> {
|
assertThrows(BadRequestException.class, () -> {
|
||||||
EnterpriseRecaptchaClient.parseInputToken(TOKEN);
|
RecaptchaClient.parseInputToken(TOKEN);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue