diff --git a/java/google/registry/env/common/pubapi/WEB-INF/web.xml b/java/google/registry/env/common/pubapi/WEB-INF/web.xml index 2fb923326..ca266e06e 100644 --- a/java/google/registry/env/common/pubapi/WEB-INF/web.xml +++ b/java/google/registry/env/common/pubapi/WEB-INF/web.xml @@ -37,15 +37,6 @@ /check - - - pubapi-servlet - /check2 - - diff --git a/java/google/registry/flows/CheckApi2Action.java b/java/google/registry/flows/CheckApi2Action.java deleted file mode 100644 index 54b9b123a..000000000 --- a/java/google/registry/flows/CheckApi2Action.java +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2018 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -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.validateDomainName; -import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables; -import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; -import static google.registry.model.registry.label.ReservationType.getTypeOfHighestSeverity; -import static google.registry.model.registry.label.ReservedList.getReservationTypes; -import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; -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; -import static google.registry.monitoring.whitebox.CheckApiMetric.Status.INVALID_REGISTRY_PHASE; -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.PREMINUM; -import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; -import static google.registry.pricing.PricingEngineProxy.isDomainPremium; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; -import static org.json.simple.JSONValue.toJSONString; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.flogger.FluentLogger; -import com.google.common.net.InternetDomainName; -import com.google.common.net.MediaType; -import dagger.Module; -import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException; -import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException; -import google.registry.model.domain.DomainResource; -import google.registry.model.index.ForeignKeyIndex; -import google.registry.model.registry.Registry; -import google.registry.model.registry.label.ReservationType; -import google.registry.monitoring.whitebox.CheckApiMetric; -import google.registry.monitoring.whitebox.CheckApiMetric.Availability; -import google.registry.request.Action; -import google.registry.request.Parameter; -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; -import org.joda.time.DateTime; - -/** - * An action that returns availability and premium checks as JSON. - * - *

This action returns plain JSON without a safety prefix, so it's vital that the output not be - * user controlled, lest it open an XSS vector. Do not modify this to return the domain name in the - * response. - */ -@Action(path = "/check2", auth = Auth.AUTH_PUBLIC_ANONYMOUS) -// TODO(b/80417678): rename this class to CheckApiAction and change path to "/check". -public class CheckApi2Action implements Runnable { - - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - @Inject - @Parameter("domain") - String domain; - - @Inject Response response; - @Inject Clock clock; - @Inject CheckApiMetric.Builder metricBuilder; - @Inject CheckApiMetrics checkApiMetrics; - - @Inject - CheckApi2Action() {} - - @Override - public void run() { - try { - response.setHeader("Content-Disposition", "attachment"); - response.setHeader("X-Content-Type-Options", "nosniff"); - response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); - response.setContentType(MediaType.JSON_UTF_8); - response.setPayload(toJSONString(doCheck())); - } finally { - CheckApiMetric metric = metricBuilder.build(); - checkApiMetrics.incrementCheckApiRequest(metric); - checkApiMetrics.recordProcessingTime(metric); - } - } - - private Map doCheck() { - String domainString; - InternetDomainName domainName; - try { - domainString = canonicalizeDomainName(nullToEmpty(domain)); - domainName = validateDomainName(domainString); - } catch (IllegalArgumentException | EppException e) { - metricBuilder.status(INVALID_NAME); - return fail("Must supply a valid domain name on an authoritative TLD"); - } - try { - // Throws an EppException with a reasonable error message which will be sent back to caller. - validateDomainNameWithIdnTables(domainName); - - DateTime now = clock.nowUtc(); - Registry registry = Registry.get(domainName.parent().toString()); - try { - verifyNotInPredelegation(registry, now); - } catch (BadCommandForRegistryPhaseException e) { - metricBuilder.status(INVALID_REGISTRY_PHASE); - return fail("Check in this TLD is not allowed in the current registry phase"); - } - - boolean isRegistered = checkExists(domainString, now); - Optional reservedError = Optional.empty(); - 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); - - ImmutableMap.Builder responseBuilder = new ImmutableMap.Builder<>(); - metricBuilder.status(SUCCESS).availability(availability); - responseBuilder.put("status", "success").put("available", availability.equals(AVAILABLE)); - - boolean isPremium = isDomainPremium(domainString, now); - metricBuilder.tier(isPremium ? PREMINUM : STANDARD); - if (availability.equals(AVAILABLE)) { - responseBuilder.put("tier", isPremium ? "premium" : "standard"); - } else { - responseBuilder.put("reason", errorMsg); - } - return responseBuilder.build(); - } catch (InvalidIdnDomainLabelException e) { - metricBuilder.status(INVALID_NAME); - return fail(e.getResult().getMsg()); - } catch (Exception e) { - metricBuilder.status(UNKNOWN_ERROR); - logger.atWarning().withCause(e).log("Unknown error"); - return fail("Invalid request"); - } - } - - private boolean checkExists(String domainString, DateTime now) { - return !ForeignKeyIndex.loadCached(DomainResource.class, ImmutableList.of(domainString), now) - .isEmpty(); - } - - private Optional checkReserved(InternetDomainName domainName) { - ImmutableSet reservationTypes = - getReservationTypes(domainName.parts().get(0), domainName.parent().toString()); - if (!reservationTypes.isEmpty()) { - return Optional.of(getTypeOfHighestSeverity(reservationTypes).getMessageForCheck()); - } - return Optional.empty(); - } - - private Map fail(String reason) { - return ImmutableMap.of("status", "error", "reason", reason); - } - - /** Dagger module for the check api endpoint. */ - @Module - public static final class CheckApi2Module { - // TODO(b/80417678): provide Parameter("domain") once CheckApiAction is replaced by this class. - } -} diff --git a/java/google/registry/flows/CheckApiAction.java b/java/google/registry/flows/CheckApiAction.java index 2a768620b..462853197 100644 --- a/java/google/registry/flows/CheckApiAction.java +++ b/java/google/registry/flows/CheckApiAction.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// Copyright 2018 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,135 +14,162 @@ package google.registry.flows; -import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Strings.nullToEmpty; -import static com.google.common.io.Resources.getResource; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; -import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS; -import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_11; -import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_12; -import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.FEE_0_6; -import static google.registry.model.registry.Registries.findTldForNameOrThrow; +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.registry.label.ReservationType.getTypeOfHighestSeverity; +import static google.registry.model.registry.label.ReservedList.getReservationTypes; +import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; +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; +import static google.registry.monitoring.whitebox.CheckApiMetric.Status.INVALID_REGISTRY_PHASE; +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.PREMINUM; +import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; +import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.util.DomainNameUtils.canonicalizeDomainName; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.json.simple.JSONValue.toJSONString; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; +import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; import com.google.common.net.InternetDomainName; import com.google.common.net.MediaType; -import com.google.template.soy.SoyFileSet; -import com.google.template.soy.tofu.SoyTofu; import dagger.Module; import dagger.Provides; -import google.registry.config.RegistryConfig.Config; -import google.registry.flows.soy.DomainCheckFeeEppSoyInfo; -import google.registry.model.domain.fee.FeeCheckResponseExtension; -import google.registry.model.eppoutput.CheckData.DomainCheck; -import google.registry.model.eppoutput.CheckData.DomainCheckData; -import google.registry.model.eppoutput.EppResponse; +import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException; +import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException; +import google.registry.model.domain.DomainResource; +import google.registry.model.index.ForeignKeyIndex; +import google.registry.model.registry.Registry; +import google.registry.model.registry.label.ReservationType; +import google.registry.monitoring.whitebox.CheckApiMetric; +import google.registry.monitoring.whitebox.CheckApiMetric.Availability; import google.registry.request.Action; 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; import javax.servlet.http.HttpServletRequest; +import org.joda.time.DateTime; /** - * A servlet that returns availability and premium checks as JSON. + * An action that returns availability and premium checks as JSON. * *

This action returns plain JSON without a safety prefix, so it's vital that the output not be * user controlled, lest it open an XSS vector. Do not modify this to return the domain name in the * response. */ -@Action( - path = "/check", - auth = Auth.AUTH_PUBLIC_ANONYMOUS -) +@Action(path = "/check", auth = Auth.AUTH_PUBLIC_ANONYMOUS) public class CheckApiAction implements Runnable { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private static final SoyTofu TOFU = - SoyFileSet.builder().add(getResource(DomainCheckFeeEppSoyInfo.class, - DomainCheckFeeEppSoyInfo.getInstance().getFileName())).build().compileToTofu(); + @Inject + @Parameter("domain") + String domain; - @Inject @Parameter("domain") String domain; @Inject Response response; - @Inject EppController eppController; - @Inject @Config("checkApiServletRegistrarClientId") String checkApiServletRegistrarClientId; - @Inject CheckApiAction() {} + @Inject Clock clock; + @Inject CheckApiMetric.Builder metricBuilder; + @Inject CheckApiMetrics checkApiMetrics; + + @Inject + CheckApiAction() {} @Override public void run() { - response.setHeader("Content-Disposition", "attachment"); - response.setHeader("X-Content-Type-Options", "nosniff"); - response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); - response.setContentType(MediaType.JSON_UTF_8); - response.setPayload(toJSONString(doCheck())); + try { + response.setHeader("Content-Disposition", "attachment"); + response.setHeader("X-Content-Type-Options", "nosniff"); + response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); + response.setContentType(MediaType.JSON_UTF_8); + response.setPayload(toJSONString(doCheck())); + } finally { + CheckApiMetric metric = metricBuilder.build(); + checkApiMetrics.incrementCheckApiRequest(metric); + checkApiMetrics.recordProcessingTime(metric); + } } private Map doCheck() { String domainString; + InternetDomainName domainName; try { domainString = canonicalizeDomainName(nullToEmpty(domain)); - // Validate the TLD. - findTldForNameOrThrow(InternetDomainName.from(domainString)); - } catch (IllegalStateException | IllegalArgumentException e) { + domainName = validateDomainName(domainString); + } catch (IllegalArgumentException | EppException e) { + metricBuilder.status(INVALID_NAME); return fail("Must supply a valid domain name on an authoritative TLD"); } try { - byte[] inputXml = TOFU - .newRenderer(DomainCheckFeeEppSoyInfo.DOMAINCHECKFEE) - .setData(ImmutableMap.of("domainName", domainString)) - .render() - .getBytes(UTF_8); - SessionMetadata sessionMetadata = - new StatelessRequestSessionMetadata(checkApiServletRegistrarClientId, FEE_EXTENSION_URIS); - EppResponse response = eppController - .handleEppCommand( - sessionMetadata, - new PasswordOnlyTransportCredentials(), - EppRequestSource.CHECK_API, - false, // This endpoint is never a dry run. - false, // This endpoint is never a superuser. - inputXml) - .getResponse(); - if (!response.getResult().getCode().isSuccess()) { - return fail(response.getResult().getMsg()); + // Throws an EppException with a reasonable error message which will be sent back to caller. + validateDomainNameWithIdnTables(domainName); + + DateTime now = clock.nowUtc(); + Registry registry = Registry.get(domainName.parent().toString()); + try { + verifyNotInPredelegation(registry, now); + } catch (BadCommandForRegistryPhaseException e) { + metricBuilder.status(INVALID_REGISTRY_PHASE); + return fail("Check in this TLD is not allowed in the current registry phase"); } - DomainCheckData checkData = (DomainCheckData) response.getResponseData().get(0); - DomainCheck check = (DomainCheck) checkData.getChecks().get(0); - boolean available = check.getName().getAvail(); - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - builder - .put("status", "success") - .put("available", available); - if (available) { - FeeCheckResponseExtension feeCheckResponseExtension = - (FeeCheckResponseExtension) response.getFirstExtensionOfType( - FEE_0_12.getResponseExtensionClass(), - FEE_0_11.getResponseExtensionClass(), - FEE_0_6.getResponseExtensionClass()); - if (feeCheckResponseExtension != null) { - builder.put("tier", - firstNonNull( - Iterables.getOnlyElement(feeCheckResponseExtension.getItems()).getFeeClass(), - "standard")); - } + + boolean isRegistered = checkExists(domainString, now); + Optional reservedError = Optional.empty(); + 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); + + ImmutableMap.Builder responseBuilder = new ImmutableMap.Builder<>(); + metricBuilder.status(SUCCESS).availability(availability); + responseBuilder.put("status", "success").put("available", availability.equals(AVAILABLE)); + + boolean isPremium = isDomainPremium(domainString, now); + metricBuilder.tier(isPremium ? PREMINUM : STANDARD); + if (availability.equals(AVAILABLE)) { + responseBuilder.put("tier", isPremium ? "premium" : "standard"); } else { - builder.put("reason", check.getReason()); + responseBuilder.put("reason", errorMsg); } - return builder.build(); + return responseBuilder.build(); + } catch (InvalidIdnDomainLabelException e) { + metricBuilder.status(INVALID_NAME); + return fail(e.getResult().getMsg()); } catch (Exception e) { + metricBuilder.status(UNKNOWN_ERROR); logger.atWarning().withCause(e).log("Unknown error"); return fail("Invalid request"); } } + private boolean checkExists(String domainString, DateTime now) { + return !ForeignKeyIndex.loadCached(DomainResource.class, ImmutableList.of(domainString), now) + .isEmpty(); + } + + private Optional checkReserved(InternetDomainName domainName) { + ImmutableSet reservationTypes = + getReservationTypes(domainName.parts().get(0), domainName.parent().toString()); + if (!reservationTypes.isEmpty()) { + return Optional.of(getTypeOfHighestSeverity(reservationTypes).getMessageForCheck()); + } + return Optional.empty(); + } + private Map fail(String reason) { return ImmutableMap.of("status", "error", "reason", reason); } @@ -150,6 +177,7 @@ public class CheckApiAction implements Runnable { /** Dagger module for the check api endpoint. */ @Module public static final class CheckApiModule { + @Provides @Parameter("domain") static String provideDomain(HttpServletRequest req) { diff --git a/java/google/registry/module/pubapi/PubApiComponent.java b/java/google/registry/module/pubapi/PubApiComponent.java index 608837997..110744e7e 100644 --- a/java/google/registry/module/pubapi/PubApiComponent.java +++ b/java/google/registry/module/pubapi/PubApiComponent.java @@ -41,7 +41,6 @@ import javax.inject.Singleton; @Singleton @Component( modules = { - // TODO(b/79692981): Remove flow-related includes once check API is rewritten to not wrap flow. AppIdentityCredentialModule.class, AuthModule.class, ConfigModule.class, diff --git a/java/google/registry/module/pubapi/PubApiRequestComponent.java b/java/google/registry/module/pubapi/PubApiRequestComponent.java index 95dd56826..be2017d0a 100644 --- a/java/google/registry/module/pubapi/PubApiRequestComponent.java +++ b/java/google/registry/module/pubapi/PubApiRequestComponent.java @@ -17,11 +17,8 @@ package google.registry.module.pubapi; import dagger.Module; import dagger.Subcomponent; import google.registry.dns.DnsModule; -import google.registry.flows.CheckApi2Action; -import google.registry.flows.CheckApi2Action.CheckApi2Module; import google.registry.flows.CheckApiAction; import google.registry.flows.CheckApiAction.CheckApiModule; -import google.registry.flows.FlowComponent; import google.registry.flows.TlsCredentials.EppTlsModule; import google.registry.monitoring.whitebox.WhiteboxModule; import google.registry.rdap.RdapAutnumAction; @@ -46,7 +43,6 @@ import google.registry.whois.WhoisModule; @Subcomponent( modules = { CheckApiModule.class, - CheckApi2Module.class, DnsModule.class, EppTlsModule.class, RdapModule.class, @@ -56,9 +52,6 @@ import google.registry.whois.WhoisModule; }) interface PubApiRequestComponent { CheckApiAction checkApiAction(); - CheckApi2Action checkApi2Action(); - // TODO(b/79692981): Remove flow-related includes once check API is rewritten to not wrap flow. - FlowComponent.Builder flowComponentBuilder(); RdapAutnumAction rdapAutnumAction(); RdapDomainAction rdapDomainAction(); RdapDomainSearchAction rdapDomainSearchAction(); diff --git a/javatests/google/registry/flows/CheckApi2ActionTest.java b/javatests/google/registry/flows/CheckApi2ActionTest.java deleted file mode 100644 index ebfa87dde..000000000 --- a/javatests/google/registry/flows/CheckApi2ActionTest.java +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2018 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.flows; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth8.assertThat; -import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; -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.PREMINUM; -import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.persistActiveDomain; -import static google.registry.testing.DatastoreHelper.persistReservedList; -import static google.registry.testing.DatastoreHelper.persistResource; -import static org.mockito.Mockito.verify; - -import google.registry.model.registry.Registry; -import google.registry.model.registry.Registry.TldState; -import google.registry.monitoring.whitebox.CheckApiMetric; -import google.registry.monitoring.whitebox.CheckApiMetric.Availability; -import google.registry.monitoring.whitebox.CheckApiMetric.Status; -import google.registry.monitoring.whitebox.CheckApiMetric.Tier; -import google.registry.testing.AppEngineRule; -import google.registry.testing.FakeClock; -import google.registry.testing.FakeResponse; -import google.registry.testing.MockitoJUnitRule; -import java.util.Map; -import org.joda.time.DateTime; -import org.json.simple.JSONValue; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; - -/** Tests for {@link CheckApi2Action}. */ -@RunWith(JUnit4.class) -public class CheckApi2ActionTest { - - private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z"); - - @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); - @Rule public final MockitoJUnitRule mocks = MockitoJUnitRule.create(); - - @Mock private CheckApiMetrics checkApiMetrics; - @Captor private ArgumentCaptor metricCaptor; - - private DateTime endTime; - - @Before - public void init() { - createTld("example"); - persistResource( - Registry.get("example") - .asBuilder() - .setReservedLists(persistReservedList("example-reserved", "foo,FULLY_BLOCKED")) - .build()); - } - - @SuppressWarnings("unchecked") - private Map getCheckResponse(String domain) { - CheckApi2Action action = new CheckApi2Action(); - 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(); - endTime = fakeClock.nowUtc(); - - action.run(); - return (Map) JSONValue.parse(((FakeResponse) action.response).getPayload()); - } - - @Test - public void testFailure_nullDomain() { - assertThat(getCheckResponse(null)) - .containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_emptyDomain() { - assertThat(getCheckResponse("")) - .containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_invalidDomain() { - assertThat(getCheckResponse("@#$%^")) - .containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_singlePartDomain() { - assertThat(getCheckResponse("foo")) - .containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_nonExistentTld() { - assertThat(getCheckResponse("foo.bar")) - .containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_invalidIdnTable() { - assertThat(getCheckResponse("ΑΒΓ.example")) - .containsExactly( - "status", "error", - "reason", "Domain label is not allowed by IDN table"); - - verifyFailureMetric(Status.INVALID_NAME); - } - - @Test - public void testFailure_tldInPredelegation() { - createTld("predelegated", TldState.PREDELEGATION); - assertThat(getCheckResponse("foo.predelegated")) - .containsExactly( - "status", "error", - "reason", "Check in this TLD is not allowed in the current registry phase"); - - verifyFailureMetric(Status.INVALID_REGISTRY_PHASE); - } - - @Test - public void testSuccess_availableStandard() { - assertThat(getCheckResponse("somedomain.example")) - .containsExactly( - "status", "success", - "available", true, - "tier", "standard"); - - verifySuccessMetric(STANDARD, AVAILABLE); - } - - @Test - public void testSuccess_availableCapital() { - assertThat(getCheckResponse("SOMEDOMAIN.EXAMPLE")) - .containsExactly( - "status", "success", - "available", true, - "tier", "standard"); - - verifySuccessMetric(STANDARD, AVAILABLE); - } - - @Test - public void testSuccess_availableUnicode() { - assertThat(getCheckResponse("ééé.example")) - .containsExactly( - "status", "success", - "available", true, - "tier", "standard"); - - verifySuccessMetric(STANDARD, AVAILABLE); - } - - @Test - public void testSuccess_availablePunycode() { - assertThat(getCheckResponse("xn--9caaa.example")) - .containsExactly( - "status", "success", - "available", true, - "tier", "standard"); - - verifySuccessMetric(STANDARD, AVAILABLE); - } - - @Test - public void testSuccess_availablePremium() { - assertThat(getCheckResponse("rich.example")) - .containsExactly( - "status", "success", - "available", true, - "tier", "premium"); - - verifySuccessMetric(PREMINUM, AVAILABLE); - } - - @Test - public void testSuccess_alreadyRegistered() { - persistActiveDomain("somedomain.example"); - assertThat(getCheckResponse("somedomain.example")) - .containsExactly( - "status", "success", - "available", false, - "reason", "In use"); - - verifySuccessMetric(STANDARD, REGISTERED); - } - - @Test - public void testSuccess_reserved() { - assertThat(getCheckResponse("foo.example")) - .containsExactly( - "status", "success", - "available", false, - "reason", "Reserved"); - - verifySuccessMetric(STANDARD, RESERVED); - } - - private void verifySuccessMetric(Tier tier, Availability availability) { - verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); - CheckApiMetric metric = metricCaptor.getValue(); - - verify(checkApiMetrics).recordProcessingTime(metric); - assertThat(metric.availability()).hasValue(availability); - assertThat(metric.tier()).hasValue(tier); - assertThat(metric.status()).isEqualTo(Status.SUCCESS); - assertThat(metric.startTimestamp()).isEqualTo(START_TIME); - assertThat(metric.endTimestamp()).isEqualTo(endTime); - } - - private void verifyFailureMetric(Status status) { - verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); - CheckApiMetric metric = metricCaptor.getValue(); - - verify(checkApiMetrics).recordProcessingTime(metric); - assertThat(metric.availability()).isEmpty(); - assertThat(metric.tier()).isEmpty(); - assertThat(metric.status()).isEqualTo(status); - assertThat(metric.startTimestamp()).isEqualTo(START_TIME); - assertThat(metric.endTimestamp()).isEqualTo(endTime); - } -} diff --git a/javatests/google/registry/flows/CheckApiActionTest.java b/javatests/google/registry/flows/CheckApiActionTest.java index 6e98c2949..f30e99c86 100644 --- a/javatests/google/registry/flows/CheckApiActionTest.java +++ b/javatests/google/registry/flows/CheckApiActionTest.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// Copyright 2018 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,35 +15,53 @@ package google.registry.flows; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; +import static google.registry.monitoring.whitebox.CheckApiMetric.Availability.AVAILABLE; +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.PREMINUM; +import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.loadRegistrar; import static google.registry.testing.DatastoreHelper.persistActiveDomain; import static google.registry.testing.DatastoreHelper.persistReservedList; import static google.registry.testing.DatastoreHelper.persistResource; +import static org.mockito.Mockito.verify; -import com.google.common.collect.ImmutableSet; -import google.registry.flows.EppTestComponent.FakesAndMocksModule; import google.registry.model.registry.Registry; +import google.registry.model.registry.Registry.TldState; +import google.registry.monitoring.whitebox.CheckApiMetric; +import google.registry.monitoring.whitebox.CheckApiMetric.Availability; +import google.registry.monitoring.whitebox.CheckApiMetric.Status; +import google.registry.monitoring.whitebox.CheckApiMetric.Tier; import google.registry.testing.AppEngineRule; +import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; +import google.registry.testing.MockitoJUnitRule; import java.util.Map; +import org.joda.time.DateTime; import org.json.simple.JSONValue; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; /** Tests for {@link CheckApiAction}. */ @RunWith(JUnit4.class) public class CheckApiActionTest { - @Rule - public final AppEngineRule appEngine = AppEngineRule.builder() - .withDatastore() - .build(); + private static final DateTime START_TIME = DateTime.parse("2000-01-01T00:00:00.0Z"); - final CheckApiAction action = new CheckApiAction(); + @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); + @Rule public final MockitoJUnitRule mocks = MockitoJUnitRule.create(); + + @Mock private CheckApiMetrics checkApiMetrics; + @Captor private ArgumentCaptor metricCaptor; + + private DateTime endTime; @Before public void init() { @@ -57,117 +75,190 @@ public class CheckApiActionTest { @SuppressWarnings("unchecked") private Map getCheckResponse(String domain) { + CheckApiAction action = new CheckApiAction(); action.domain = domain; action.response = new FakeResponse(); - action.checkApiServletRegistrarClientId = "TheRegistrar"; - action.eppController = DaggerEppTestComponent.builder() - .fakesAndMocksModule(FakesAndMocksModule.create()) - .build() - .startRequest() - .eppController(); + FakeClock fakeClock = new FakeClock(START_TIME); + action.clock = fakeClock; + action.metricBuilder = CheckApiMetric.builder(fakeClock); + action.checkApiMetrics = checkApiMetrics; + fakeClock.advanceOneMilli(); + endTime = fakeClock.nowUtc(); + action.run(); return (Map) JSONValue.parse(((FakeResponse) action.response).getPayload()); } @Test public void testFailure_nullDomain() { - assertThat(getCheckResponse(null)).containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); + assertThat(getCheckResponse(null)) + .containsExactly( + "status", "error", + "reason", "Must supply a valid domain name on an authoritative TLD"); + + verifyFailureMetric(Status.INVALID_NAME); } @Test public void testFailure_emptyDomain() { - assertThat(getCheckResponse("")).containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); + assertThat(getCheckResponse("")) + .containsExactly( + "status", "error", + "reason", "Must supply a valid domain name on an authoritative TLD"); + + verifyFailureMetric(Status.INVALID_NAME); } @Test public void testFailure_invalidDomain() { - assertThat(getCheckResponse("@#$%^")).containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); + assertThat(getCheckResponse("@#$%^")) + .containsExactly( + "status", "error", + "reason", "Must supply a valid domain name on an authoritative TLD"); + + verifyFailureMetric(Status.INVALID_NAME); } @Test public void testFailure_singlePartDomain() { - assertThat(getCheckResponse("foo")).containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); + assertThat(getCheckResponse("foo")) + .containsExactly( + "status", "error", + "reason", "Must supply a valid domain name on an authoritative TLD"); + + verifyFailureMetric(Status.INVALID_NAME); } @Test public void testFailure_nonExistentTld() { - assertThat(getCheckResponse("foo.bar")).containsExactly( - "status", "error", - "reason", "Must supply a valid domain name on an authoritative TLD"); + assertThat(getCheckResponse("foo.bar")) + .containsExactly( + "status", "error", + "reason", "Must supply a valid domain name on an authoritative TLD"); + + verifyFailureMetric(Status.INVALID_NAME); } @Test - public void testFailure_unauthorizedTld() { - createTld("foo"); - persistResource( - loadRegistrar("TheRegistrar").asBuilder().setAllowedTlds(ImmutableSet.of("foo")).build()); - assertThat(getCheckResponse("timmy.example")).containsExactly( - "status", "error", - "reason", "Registrar is not authorized to access the TLD example"); + public void testFailure_invalidIdnTable() { + assertThat(getCheckResponse("ΑΒΓ.example")) + .containsExactly( + "status", "error", + "reason", "Domain label is not allowed by IDN table"); + + verifyFailureMetric(Status.INVALID_NAME); + } + + @Test + public void testFailure_tldInPredelegation() { + createTld("predelegated", TldState.PREDELEGATION); + assertThat(getCheckResponse("foo.predelegated")) + .containsExactly( + "status", "error", + "reason", "Check in this TLD is not allowed in the current registry phase"); + + verifyFailureMetric(Status.INVALID_REGISTRY_PHASE); } @Test public void testSuccess_availableStandard() { - assertThat(getCheckResponse("somedomain.example")).containsExactly( - "status", "success", - "available", true, - "tier", "standard"); + assertThat(getCheckResponse("somedomain.example")) + .containsExactly( + "status", "success", + "available", true, + "tier", "standard"); + + verifySuccessMetric(STANDARD, AVAILABLE); } @Test public void testSuccess_availableCapital() { - assertThat(getCheckResponse("SOMEDOMAIN.EXAMPLE")).containsExactly( - "status", "success", - "available", true, - "tier", "standard"); + assertThat(getCheckResponse("SOMEDOMAIN.EXAMPLE")) + .containsExactly( + "status", "success", + "available", true, + "tier", "standard"); + + verifySuccessMetric(STANDARD, AVAILABLE); } @Test public void testSuccess_availableUnicode() { - assertThat(getCheckResponse("ééé.example")).containsExactly( - "status", "success", - "available", true, - "tier", "standard"); + assertThat(getCheckResponse("ééé.example")) + .containsExactly( + "status", "success", + "available", true, + "tier", "standard"); + + verifySuccessMetric(STANDARD, AVAILABLE); } @Test public void testSuccess_availablePunycode() { - assertThat(getCheckResponse("xn--9caaa.example")).containsExactly( - "status", "success", - "available", true, - "tier", "standard"); + assertThat(getCheckResponse("xn--9caaa.example")) + .containsExactly( + "status", "success", + "available", true, + "tier", "standard"); + + verifySuccessMetric(STANDARD, AVAILABLE); } @Test public void testSuccess_availablePremium() { - assertThat(getCheckResponse("rich.example")).containsExactly( - "status", "success", - "available", true, - "tier", "premium"); + assertThat(getCheckResponse("rich.example")) + .containsExactly( + "status", "success", + "available", true, + "tier", "premium"); + + verifySuccessMetric(PREMINUM, AVAILABLE); } @Test public void testSuccess_alreadyRegistered() { persistActiveDomain("somedomain.example"); - assertThat(getCheckResponse("somedomain.example")).containsExactly( - "status", "success", - "available", false, - "reason", "In use"); + assertThat(getCheckResponse("somedomain.example")) + .containsExactly( + "status", "success", + "available", false, + "reason", "In use"); + + verifySuccessMetric(STANDARD, REGISTERED); } @Test public void testSuccess_reserved() { - assertThat(getCheckResponse("foo.example")).containsExactly( - "status", "success", - "available", false, - "reason", "Reserved"); + assertThat(getCheckResponse("foo.example")) + .containsExactly( + "status", "success", + "available", false, + "reason", "Reserved"); + + verifySuccessMetric(STANDARD, RESERVED); + } + + private void verifySuccessMetric(Tier tier, Availability availability) { + verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); + CheckApiMetric metric = metricCaptor.getValue(); + + verify(checkApiMetrics).recordProcessingTime(metric); + assertThat(metric.availability()).hasValue(availability); + assertThat(metric.tier()).hasValue(tier); + assertThat(metric.status()).isEqualTo(Status.SUCCESS); + assertThat(metric.startTimestamp()).isEqualTo(START_TIME); + assertThat(metric.endTimestamp()).isEqualTo(endTime); + } + + private void verifyFailureMetric(Status status) { + verify(checkApiMetrics).incrementCheckApiRequest(metricCaptor.capture()); + CheckApiMetric metric = metricCaptor.getValue(); + + verify(checkApiMetrics).recordProcessingTime(metric); + assertThat(metric.availability()).isEmpty(); + assertThat(metric.tier()).isEmpty(); + assertThat(metric.status()).isEqualTo(status); + assertThat(metric.startTimestamp()).isEqualTo(START_TIME); + assertThat(metric.endTimestamp()).isEqualTo(endTime); } } diff --git a/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt b/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt index 89cf2d5b0..089d67c16 100644 --- a/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt +++ b/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt @@ -1,7 +1,6 @@ PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY /_dr/whois WhoisAction POST n INTERNAL,API APP PUBLIC /check CheckApiAction GET n INTERNAL NONE PUBLIC -/check2 CheckApi2Action GET n INTERNAL NONE PUBLIC /rdap/autnum/(*) RdapAutnumAction GET,HEAD n INTERNAL NONE PUBLIC /rdap/domain/(*) RdapDomainAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC /rdap/domains RdapDomainSearchAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC