From f8ac7afc33a10dfed2d410ea2efcd8f02b4ae444 Mon Sep 17 00:00:00 2001 From: Weimin Yu Date: Tue, 9 Jan 2024 13:19:07 -0500 Subject: [PATCH] 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. --- core/build.gradle | 1 - .../google/registry/flows/CheckApiAction.java | 37 ++++++++++++++++--- .../monitoring/whitebox/CheckApiMetric.java | 1 + .../registry/flows/CheckApiActionTest.java | 24 ++++++++++-- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c1315e9ac..797580b32 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -38,7 +38,6 @@ def jsDir = "${project.projectDir}/src/main/javascript" def outcastTestPatterns = [ // Problem seems to lie with AppEngine TaskQueue for test. "google/registry/batch/RefreshDnsOnHostRenameActionTest.*", - "google/registry/flows/CheckApiActionTest.*", "google/registry/flows/EppLifecycleHostTest.*", "google/registry/flows/domain/DomainCreateFlowTest.*", "google/registry/flows/domain/DomainUpdateFlowTest.*", diff --git a/core/src/main/java/google/registry/flows/CheckApiAction.java b/core/src/main/java/google/registry/flows/CheckApiAction.java index 7f27a58f5..21aa726b4 100644 --- a/core/src/main/java/google/registry/flows/CheckApiAction.java +++ b/core/src/main/java/google/registry/flows/CheckApiAction.java @@ -16,12 +16,14 @@ package google.registry.flows; import static com.google.common.base.Strings.nullToEmpty; 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.validateDomainNameWithIdnTables; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity; 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.BSA_BLOCKED; 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.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.Tier.PREMIUM; 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.util.DomainNameUtils.canonicalizeHostname; 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.Response; import google.registry.request.auth.Auth; -import google.registry.util.Clock; import java.util.Map; import java.util.Optional; import javax.inject.Inject; @@ -79,7 +81,6 @@ public class CheckApiAction implements Runnable { String domain; @Inject Response response; - @Inject Clock clock; @Inject CheckApiMetric.Builder metricBuilder; @Inject CheckApiMetrics checkApiMetrics; @@ -104,6 +105,20 @@ public class CheckApiAction implements Runnable { private Map doCheck() { String domainString; 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 checkDomainName(InternetDomainName domainName) { + tm().assertInTransaction(); + + String domainString; try { domainString = canonicalizeHostname(nullToEmpty(domain)); 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. validateDomainNameWithIdnTables(domainName); - DateTime now = clock.nowUtc(); + DateTime now = tm().getTransactionTime(); Tld tld = Tld.get(domainName.parent().toString()); try { verifyNotInPredelegation(tld, now); @@ -126,13 +141,25 @@ public class CheckApiAction implements Runnable { boolean isRegistered = checkExists(domainString, now); Optional reservedError = Optional.empty(); + boolean isBsaBlocked = false; boolean isReserved = false; if (!isRegistered) { reservedError = checkReserved(domainName); isReserved = reservedError.isPresent(); } - Availability availability = isRegistered ? REGISTERED : (isReserved ? RESERVED : AVAILABLE); - String errorMsg = isRegistered ? "In use" : (isReserved ? reservedError.get() : null); + if (!isRegistered && !isReserved) { + 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 responseBuilder = new ImmutableMap.Builder<>(); metricBuilder.status(SUCCESS).availability(availability); diff --git a/core/src/main/java/google/registry/monitoring/whitebox/CheckApiMetric.java b/core/src/main/java/google/registry/monitoring/whitebox/CheckApiMetric.java index 5ace99012..a59799fd1 100644 --- a/core/src/main/java/google/registry/monitoring/whitebox/CheckApiMetric.java +++ b/core/src/main/java/google/registry/monitoring/whitebox/CheckApiMetric.java @@ -45,6 +45,7 @@ public abstract class CheckApiMetric { public enum Availability { RESERVED("reserved"), REGISTERED("registered"), + BSA_BLOCKED("blocked"), AVAILABLE("available"); private final String displayLabel; diff --git a/core/src/test/java/google/registry/flows/CheckApiActionTest.java b/core/src/test/java/google/registry/flows/CheckApiActionTest.java index 15e6a8ed1..162e98d99 100644 --- a/core/src/test/java/google/registry/flows/CheckApiActionTest.java +++ b/core/src/test/java/google/registry/flows/CheckApiActionTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; 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.BSA_BLOCKED; 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.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.persistReservedList; import static google.registry.testing.DatabaseHelper.persistResource; +import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.mockito.Mockito.verify; +import google.registry.bsa.persistence.BsaLabelTestingUtils; import google.registry.model.tld.Tld; import google.registry.monitoring.whitebox.CheckApiMetric; 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.FakeResponse; import java.util.Map; +import java.util.Optional; import org.joda.time.DateTime; import org.json.simple.JSONValue; import org.junit.jupiter.api.BeforeEach; @@ -54,10 +58,11 @@ import org.mockito.junit.jupiter.MockitoExtension; class CheckApiActionTest { private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z"); + private final FakeClock fakeClock = new FakeClock(START_TIME); @RegisterExtension final JpaIntegrationTestExtension jpa = - new JpaTestExtensions.Builder().buildIntegrationTestExtension(); + new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension(); @Mock private CheckApiMetrics checkApiMetrics; @Captor private ArgumentCaptor metricCaptor; @@ -84,8 +89,6 @@ class CheckApiActionTest { CheckApiAction action = new CheckApiAction(); action.domain = domain; action.response = new FakeResponse(); - FakeClock fakeClock = new FakeClock(START_TIME); - action.clock = fakeClock; action.metricBuilder = CheckApiMetric.builder(fakeClock); action.checkApiMetrics = checkApiMetrics; fakeClock.advanceOneMilli(); @@ -283,6 +286,21 @@ class CheckApiActionTest { 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) { verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); CheckApiMetric metric = metricCaptor.getValue();