Check BSA block status in CheckApi (#2271)

* Check BSA block status in CheckApi

Checks for and reports BSA block status if the name is not registered or
reserved.

Also moves CheckApiActionTest to standardTest. Whatever problem forcing
it to another suite has apparently disappeared.
This commit is contained in:
Weimin Yu 2024-01-09 13:19:07 -05:00 committed by GitHub
parent e56e751652
commit f8ac7afc33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 9 deletions

View file

@ -38,7 +38,6 @@ def jsDir = "${project.projectDir}/src/main/javascript"
def outcastTestPatterns = [ def outcastTestPatterns = [
// Problem seems to lie with AppEngine TaskQueue for test. // Problem seems to lie with AppEngine TaskQueue for test.
"google/registry/batch/RefreshDnsOnHostRenameActionTest.*", "google/registry/batch/RefreshDnsOnHostRenameActionTest.*",
"google/registry/flows/CheckApiActionTest.*",
"google/registry/flows/EppLifecycleHostTest.*", "google/registry/flows/EppLifecycleHostTest.*",
"google/registry/flows/domain/DomainCreateFlowTest.*", "google/registry/flows/domain/DomainCreateFlowTest.*",
"google/registry/flows/domain/DomainUpdateFlowTest.*", "google/registry/flows/domain/DomainUpdateFlowTest.*",

View file

@ -16,12 +16,14 @@ package google.registry.flows;
import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
import static google.registry.flows.domain.DomainFlowUtils.isBlockedByBsa;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName; import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables; import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity; import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity;
import static google.registry.model.tld.label.ReservedList.getReservationTypes; import static google.registry.model.tld.label.ReservedList.getReservationTypes;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.BSA_BLOCKED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.REGISTERED; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.REGISTERED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.RESERVED; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.RESERVED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Status.INVALID_NAME; import static google.registry.monitoring.whitebox.CheckApiMetric.Status.INVALID_NAME;
@ -30,6 +32,7 @@ import static google.registry.monitoring.whitebox.CheckApiMetric.Status.SUCCESS;
import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_ERROR; import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_ERROR;
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM;
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static org.json.simple.JSONValue.toJSONString; import static org.json.simple.JSONValue.toJSONString;
@ -55,7 +58,6 @@ import google.registry.request.Parameter;
import google.registry.request.RequestParameters; import google.registry.request.RequestParameters;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.request.auth.Auth; import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
@ -79,7 +81,6 @@ public class CheckApiAction implements Runnable {
String domain; String domain;
@Inject Response response; @Inject Response response;
@Inject Clock clock;
@Inject CheckApiMetric.Builder metricBuilder; @Inject CheckApiMetric.Builder metricBuilder;
@Inject CheckApiMetrics checkApiMetrics; @Inject CheckApiMetrics checkApiMetrics;
@ -104,6 +105,20 @@ public class CheckApiAction implements Runnable {
private Map<String, Object> doCheck() { private Map<String, Object> doCheck() {
String domainString; String domainString;
InternetDomainName domainName; InternetDomainName domainName;
try {
domainString = canonicalizeHostname(nullToEmpty(domain));
domainName = validateDomainName(domainString);
return tm().transact(() -> checkDomainName(domainName));
} catch (IllegalArgumentException | EppException e) {
metricBuilder.status(INVALID_NAME);
return fail("Must supply a valid domain name on an authoritative TLD");
}
}
private Map<String, Object> checkDomainName(InternetDomainName domainName) {
tm().assertInTransaction();
String domainString;
try { try {
domainString = canonicalizeHostname(nullToEmpty(domain)); domainString = canonicalizeHostname(nullToEmpty(domain));
domainName = validateDomainName(domainString); domainName = validateDomainName(domainString);
@ -115,7 +130,7 @@ public class CheckApiAction implements Runnable {
// Throws an EppException with a reasonable error message which will be sent back to caller. // Throws an EppException with a reasonable error message which will be sent back to caller.
validateDomainNameWithIdnTables(domainName); validateDomainNameWithIdnTables(domainName);
DateTime now = clock.nowUtc(); DateTime now = tm().getTransactionTime();
Tld tld = Tld.get(domainName.parent().toString()); Tld tld = Tld.get(domainName.parent().toString());
try { try {
verifyNotInPredelegation(tld, now); verifyNotInPredelegation(tld, now);
@ -126,13 +141,25 @@ public class CheckApiAction implements Runnable {
boolean isRegistered = checkExists(domainString, now); boolean isRegistered = checkExists(domainString, now);
Optional<String> reservedError = Optional.empty(); Optional<String> reservedError = Optional.empty();
boolean isBsaBlocked = false;
boolean isReserved = false; boolean isReserved = false;
if (!isRegistered) { if (!isRegistered) {
reservedError = checkReserved(domainName); reservedError = checkReserved(domainName);
isReserved = reservedError.isPresent(); isReserved = reservedError.isPresent();
} }
Availability availability = isRegistered ? REGISTERED : (isReserved ? RESERVED : AVAILABLE); if (!isRegistered && !isReserved) {
String errorMsg = isRegistered ? "In use" : (isReserved ? reservedError.get() : null); isBsaBlocked = isBlockedByBsa(domainName.parts().get(0), tld, now);
}
Availability availability =
isRegistered
? REGISTERED
: (isReserved ? RESERVED : (isBsaBlocked ? BSA_BLOCKED : AVAILABLE));
String errorMsg =
isRegistered
? "In use"
: (isReserved
? reservedError.get()
: (isBsaBlocked ? "Blocked by the Brand Safety Alliance" : null));
ImmutableMap.Builder<String, Object> responseBuilder = new ImmutableMap.Builder<>(); ImmutableMap.Builder<String, Object> responseBuilder = new ImmutableMap.Builder<>();
metricBuilder.status(SUCCESS).availability(availability); metricBuilder.status(SUCCESS).availability(availability);

View file

@ -45,6 +45,7 @@ public abstract class CheckApiMetric {
public enum Availability { public enum Availability {
RESERVED("reserved"), RESERVED("reserved"),
REGISTERED("registered"), REGISTERED("registered"),
BSA_BLOCKED("blocked"),
AVAILABLE("available"); AVAILABLE("available");
private final String displayLabel; private final String displayLabel;

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat; import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.tld.Tld.TldState.PREDELEGATION; import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.BSA_BLOCKED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.REGISTERED; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.REGISTERED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.RESERVED; import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.RESERVED;
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM;
@ -26,8 +27,10 @@ import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain; import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistReservedList; import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import google.registry.bsa.persistence.BsaLabelTestingUtils;
import google.registry.model.tld.Tld; import google.registry.model.tld.Tld;
import google.registry.monitoring.whitebox.CheckApiMetric; import google.registry.monitoring.whitebox.CheckApiMetric;
import google.registry.monitoring.whitebox.CheckApiMetric.Availability; import google.registry.monitoring.whitebox.CheckApiMetric.Availability;
@ -38,6 +41,7 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -54,10 +58,11 @@ import org.mockito.junit.jupiter.MockitoExtension;
class CheckApiActionTest { class CheckApiActionTest {
private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z"); private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z");
private final FakeClock fakeClock = new FakeClock(START_TIME);
@RegisterExtension @RegisterExtension
final JpaIntegrationTestExtension jpa = final JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationTestExtension(); new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
@Mock private CheckApiMetrics checkApiMetrics; @Mock private CheckApiMetrics checkApiMetrics;
@Captor private ArgumentCaptor<CheckApiMetric> metricCaptor; @Captor private ArgumentCaptor<CheckApiMetric> metricCaptor;
@ -84,8 +89,6 @@ class CheckApiActionTest {
CheckApiAction action = new CheckApiAction(); CheckApiAction action = new CheckApiAction();
action.domain = domain; action.domain = domain;
action.response = new FakeResponse(); action.response = new FakeResponse();
FakeClock fakeClock = new FakeClock(START_TIME);
action.clock = fakeClock;
action.metricBuilder = CheckApiMetric.builder(fakeClock); action.metricBuilder = CheckApiMetric.builder(fakeClock);
action.checkApiMetrics = checkApiMetrics; action.checkApiMetrics = checkApiMetrics;
fakeClock.advanceOneMilli(); fakeClock.advanceOneMilli();
@ -283,6 +286,21 @@ class CheckApiActionTest {
verifySuccessMetric(PREMIUM, RESERVED); verifySuccessMetric(PREMIUM, RESERVED);
} }
@Test
void testSuccess_blockedByBsa() {
BsaLabelTestingUtils.persistBsaLabel("rich", START_OF_TIME);
persistResource(
Tld.get("example").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
assertThat(getCheckResponse("rich.example"))
.containsExactly(
"tier", "premium",
"status", "success",
"available", false,
"reason", "Blocked by the Brand Safety Alliance");
verifySuccessMetric(PREMIUM, BSA_BLOCKED);
}
private void verifySuccessMetric(Tier tier, Availability availability) { private void verifySuccessMetric(Tier tier, Availability availability) {
verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture());
CheckApiMetric metric = metricCaptor.getValue(); CheckApiMetric metric = metricCaptor.getValue();