From 1c4e79f99eb8352907eb3669076043ff8ed5d0f8 Mon Sep 17 00:00:00 2001 From: mcilwain Date: Fri, 29 Sep 2017 07:42:40 -0700 Subject: [PATCH] Don't allow non-active registrars to create domains or applications Specifically, this prevents suspended registrars from creating domains or applications. Pending registrars already can't perform these actions because they get an error message when attempting to log in. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170481338 --- docs/flows.md | 2 ++ .../export/SyncGroupMembersAction.java | 2 +- .../domain/DomainApplicationCreateFlow.java | 3 +++ .../flows/domain/DomainCreateFlow.java | 3 +++ .../flows/domain/DomainFlowUtils.java | 22 ++++++++++++++++ .../registry/model/registrar/Registrar.java | 25 +++++++++++-------- java/google/registry/rdap/RdapActionBase.java | 2 +- .../registry/rdap/RdapEntityAction.java | 2 +- .../registry/rdap/RdapEntitySearchAction.java | 4 +-- .../registry/rdap/RdapJsonFormatter.java | 2 +- .../registry/tools/VerifyOteCommand.java | 2 +- .../whois/RegistrarLookupCommand.java | 2 +- .../DomainApplicationCreateFlowTest.java | 19 ++++++++++++++ .../flows/domain/DomainCreateFlowTest.java | 20 +++++++++++++-- 14 files changed, 89 insertions(+), 21 deletions(-) diff --git a/docs/flows.md b/docs/flows.md index 7198970d7..d9a5b94ef 100644 --- a/docs/flows.md +++ b/docs/flows.md @@ -314,6 +314,7 @@ An EPP flow that creates a new application for a domain resource. * Specified extension is not implemented. * 2201 * Registrar is not authorized to access this TLD. + * Registrar must be active in order to create domains or applications. * 2202 * Invalid limited registration period token. * 2302 @@ -546,6 +547,7 @@ An EPP flow that creates a new domain resource. * 2201 * Only a tool can pass a metadata extension. * Registrar is not authorized to access this TLD. + * Registrar must be active in order to create domains or applications. * 2202 * Invalid limited registration period token. * 2302 diff --git a/java/google/registry/export/SyncGroupMembersAction.java b/java/google/registry/export/SyncGroupMembersAction.java index 2d6f399e4..b81e2d730 100644 --- a/java/google/registry/export/SyncGroupMembersAction.java +++ b/java/google/registry/export/SyncGroupMembersAction.java @@ -123,7 +123,7 @@ public final class SyncGroupMembersAction implements Runnable { @Override public boolean apply(Registrar registrar) { // Only grab active registrars that require syncing and are of the correct type. - return registrar.isActive() + return registrar.isLive() && registrar.getContactsRequireSyncing() && registrar.getType() == Registrar.Type.REAL; }}) diff --git a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java index 8c0012d86..25147280d 100644 --- a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java @@ -35,6 +35,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatc import static google.registry.flows.domain.DomainFlowUtils.verifyNoCodeMarks; import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; +import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive; import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.model.EppResourceUtils.createDomainRepoId; @@ -147,6 +148,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} + * @error {@link DomainFlowUtils.RegistrarMustBeActiveToCreateDomainsException} * @error {@link DomainFlowTmchUtils.SignedMarksMustBeEncodedException} * @error {@link DomainFlowTmchUtils.SignedMarkCertificateExpiredException} * @error {@link DomainFlowTmchUtils.SignedMarkCertificateInvalidException} @@ -194,6 +196,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow { customLogic.beforeValidation(); extensionManager.validate(); validateClientIsLoggedIn(clientId); + verifyRegistrarIsActive(clientId); DateTime now = ofy().getTransactionTime(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); // Fail if the domain is already registered (e.g. this is a landrush application but the domain diff --git a/java/google/registry/flows/domain/DomainCreateFlow.java b/java/google/registry/flows/domain/DomainCreateFlow.java index c57de6250..21d83ddf6 100644 --- a/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/java/google/registry/flows/domain/DomainCreateFlow.java @@ -35,6 +35,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatc import static google.registry.flows.domain.DomainFlowUtils.verifyNoCodeMarks; import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; +import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.model.EppResourceUtils.createDomainRepoId; import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED; @@ -155,6 +156,7 @@ import org.joda.time.Duration; * @error {@link NameserversNotSpecifiedForTldWithNameserverWhitelistException} * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException} + * @error {@link DomainFlowUtils.RegistrarMustBeActiveToCreateDomainsException} * @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} @@ -196,6 +198,7 @@ public class DomainCreateFlow implements TransactionalFlow { customLogic.beforeValidation(); extensionManager.validate(); validateClientIsLoggedIn(clientId); + verifyRegistrarIsActive(clientId); DateTime now = ofy().getTransactionTime(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); Period period = command.getPeriod(); diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index 955d4618c..bc21e9f45 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -102,6 +102,7 @@ import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.host.HostResource; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservationType; @@ -753,6 +754,19 @@ public class DomainFlowUtils { } } + /** + * Check that the registrar with the given client ID is active. + * + *

Non-active registrars are not allowed to create domain applications or domain resources. + */ + static void verifyRegistrarIsActive(String clientId) + throws RegistrarMustBeActiveToCreateDomainsException { + Registrar registrar = Registrar.loadByClientIdCached(clientId).get(); + if (registrar.getState() != State.ACTIVE) { + throw new RegistrarMustBeActiveToCreateDomainsException(); + } + } + /** Check that the registry phase is not incompatible with launch extension flows. */ static void verifyRegistryStateAllowsLaunchFlows(Registry registry, DateTime now) throws BadCommandForRegistryPhaseException { @@ -1426,4 +1440,12 @@ public class DomainFlowUtils { MAX_REGISTRATION_YEARS)); } } + + /** Registrar must be active in order to create domains or applications. */ + static class RegistrarMustBeActiveToCreateDomainsException extends AuthorizationErrorException { + public RegistrarMustBeActiveToCreateDomainsException() { + super("Registrar must be active in order to create domains or applications"); + } + } + } diff --git a/java/google/registry/model/registrar/Registrar.java b/java/google/registry/model/registrar/Registrar.java index f6255733c..4d27b3a59 100644 --- a/java/google/registry/model/registrar/Registrar.java +++ b/java/google/registry/model/registrar/Registrar.java @@ -145,10 +145,8 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable /** Represents the state of a persisted registrar entity. */ public enum State { - /** - * This registrar is provisioned and may have access to the testing environment, but is not yet - * allowed to access the production environment. - */ + + /** This registrar is provisioned but not yet active, and cannot log in. */ PENDING, /** This is an active registrar account which is allowed to provision and modify domains. */ @@ -177,8 +175,8 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable /** Regex for telephone support passcode (5 digit string). */ public static final Pattern PHONE_PASSCODE_PATTERN = Pattern.compile("\\d{5}"); - /** The states in which a {@link Registrar} is considered {@link #isActive active}. */ - private static final ImmutableSet ACTIVE_STATES = + /** The states in which a {@link Registrar} is considered {@link #isLive live}. */ + private static final ImmutableSet LIVE_STATES = Sets.immutableEnumSet(State.ACTIVE, State.SUSPENDED); /** @@ -486,14 +484,19 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable return nullToEmptyImmutableSortedCopy(allowedTlds); } - /** Returns {@code true} if registrar is active. */ - public boolean isActive() { - return ACTIVE_STATES.contains(state); + /** + * Returns {@code true} if the registrar is live. + * + *

A live registrar is one that can have live domains/contacts/hosts in the registry, meaning + * that it is either currently active or used to be active (i.e. suspended). + */ + public boolean isLive() { + return LIVE_STATES.contains(state); } /** Returns {@code true} if registrar should be visible in WHOIS results. */ - public boolean isActiveAndPubliclyVisible() { - return ACTIVE_STATES.contains(state) && PUBLICLY_VISIBLE_TYPES.contains(type); + public boolean isLiveAndPubliclyVisible() { + return LIVE_STATES.contains(state) && PUBLICLY_VISIBLE_TYPES.contains(type); } public String getClientCertificate() { diff --git a/java/google/registry/rdap/RdapActionBase.java b/java/google/registry/rdap/RdapActionBase.java index af4a073f6..52ece11ed 100644 --- a/java/google/registry/rdap/RdapActionBase.java +++ b/java/google/registry/rdap/RdapActionBase.java @@ -239,7 +239,7 @@ public abstract class RdapActionBase implements Runnable { * 2. The request did not specify a registrar to filter on, or the registrar matches. */ boolean shouldBeVisible(Registrar registrar) { - return (registrar.isActiveAndPubliclyVisible() + return (registrar.isLiveAndPubliclyVisible() || (shouldIncludeDeleted() && getAuthorization().isAuthorizedForClientId(registrar.getClientId()))) && (!registrarParam.isPresent() || registrarParam.get().equals(registrar.getClientId())); diff --git a/java/google/registry/rdap/RdapEntityAction.java b/java/google/registry/rdap/RdapEntityAction.java index 3fd51f2ed..169656794 100644 --- a/java/google/registry/rdap/RdapEntityAction.java +++ b/java/google/registry/rdap/RdapEntityAction.java @@ -101,7 +101,7 @@ public class RdapEntityAction extends RdapActionBase { if (ianaIdentifier != null) { wasValidKey = true; Optional registrar = getRegistrarByIanaIdentifier(ianaIdentifier); - if ((registrar.isPresent()) && registrar.get().isActiveAndPubliclyVisible()) { + if ((registrar.isPresent()) && registrar.get().isLiveAndPubliclyVisible()) { return rdapJsonFormatter.makeRdapJsonForRegistrar( registrar.get(), true, rdapLinkBase, rdapWhoisServer, now, OutputDataType.FULL); } diff --git a/java/google/registry/rdap/RdapEntitySearchAction.java b/java/google/registry/rdap/RdapEntitySearchAction.java index e2c9c22ef..20868476e 100644 --- a/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/java/google/registry/rdap/RdapEntitySearchAction.java @@ -243,7 +243,7 @@ public class RdapEntitySearchAction extends RdapActionBase { } else { outputDataType = OutputDataType.FULL; for (Registrar registrar : registrars) { - if (registrar.isActiveAndPubliclyVisible()) { + if (registrar.isLiveAndPubliclyVisible()) { numEntities++; if (numEntities > 1) { outputDataType = OutputDataType.SUMMARY; @@ -276,7 +276,7 @@ public class RdapEntitySearchAction extends RdapActionBase { authorization)); } for (Registrar registrar : registrars) { - if (registrar.isActiveAndPubliclyVisible()) { + if (registrar.isLiveAndPubliclyVisible()) { if (jsonOutputList.size() >= rdapResultSetMaxSize) { return RdapSearchResults.create( ImmutableList.copyOf(jsonOutputList), IncompletenessWarningType.TRUNCATED); diff --git a/java/google/registry/rdap/RdapJsonFormatter.java b/java/google/registry/rdap/RdapJsonFormatter.java index 270fc3b34..88338fb2b 100644 --- a/java/google/registry/rdap/RdapJsonFormatter.java +++ b/java/google/registry/rdap/RdapJsonFormatter.java @@ -768,7 +768,7 @@ public class RdapJsonFormatter { ImmutableMap.Builder jsonBuilder = new ImmutableMap.Builder<>(); jsonBuilder.put("objectClassName", "entity"); jsonBuilder.put("handle", registrar.getIanaIdentifier().toString()); - jsonBuilder.put("status", registrar.isActive() ? STATUS_LIST_ACTIVE : STATUS_LIST_REMOVED); + jsonBuilder.put("status", registrar.isLive() ? STATUS_LIST_ACTIVE : STATUS_LIST_REMOVED); jsonBuilder.put("roles", ImmutableList.of(RdapEntityRole.REGISTRAR.rfc7483String)); jsonBuilder.put("links", ImmutableList.of(makeLink("entity", registrar.getIanaIdentifier().toString(), linkBase))); diff --git a/java/google/registry/tools/VerifyOteCommand.java b/java/google/registry/tools/VerifyOteCommand.java index e76c255b3..3e258dd7b 100644 --- a/java/google/registry/tools/VerifyOteCommand.java +++ b/java/google/registry/tools/VerifyOteCommand.java @@ -109,7 +109,7 @@ final class VerifyOteCommand implements ServerSideCommand { .transform(new Function() { @Override public String apply(Registrar registrar) { - if (!registrar.isActive()) { + if (!registrar.isLive()) { return null; } String name = registrar.getClientId(); diff --git a/java/google/registry/whois/RegistrarLookupCommand.java b/java/google/registry/whois/RegistrarLookupCommand.java index 8f26dd0e8..65e4fa930 100644 --- a/java/google/registry/whois/RegistrarLookupCommand.java +++ b/java/google/registry/whois/RegistrarLookupCommand.java @@ -51,7 +51,7 @@ final class RegistrarLookupCommand implements WhoisCommand { Map map = new HashMap<>(); // Use the normalized registrar name as a key, and ignore inactive and hidden registrars. for (Registrar registrar : Registrar.loadAllCached()) { - if (!registrar.isActiveAndPubliclyVisible() || registrar.getRegistrarName() == null) { + if (!registrar.isLiveAndPubliclyVisible() || registrar.getRegistrarName() == null) { continue; } String normalized = normalizeRegistrarName(registrar.getRegistrarName()); diff --git a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java index d1f2ce6c7..f9c2d7b0f 100644 --- a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java @@ -98,6 +98,7 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTl import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException; +import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveToCreateDomainsException; import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException; import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException; import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException; @@ -112,6 +113,8 @@ import google.registry.model.domain.launch.ApplicationStatus; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchPhase; import google.registry.model.domain.secdns.DelegationSignerData; +import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservedList; @@ -723,6 +726,22 @@ public class DomainApplicationCreateFlowTest runFlow(); } + @Test + public void testFailure_suspendedRegistrarCantCreateDomainApplication() throws Exception { + setEppInput("domain_create_sunrise_encoded_signed_mark.xml"); + persistContactsAndHosts(); + clock.advanceOneMilli(); + persistResource( + Registrar.loadByClientId("TheRegistrar") + .get() + .asBuilder() + .setState(State.SUSPENDED) + .build()); + clock.advanceOneMilli(); + thrown.expect(RegistrarMustBeActiveToCreateDomainsException.class); + runFlow(); + } + @Test public void testFailure_sunriseApplicationInLandrush() throws Exception { createTld("tld", TldState.LANDRUSH); diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index 4770342ad..4ecbef0f8 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -104,6 +104,7 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTl import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException; +import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveToCreateDomainsException; import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException; import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException; import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException; @@ -125,6 +126,8 @@ import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.eppcommon.StatusValue; import google.registry.model.poll.PollMessage; +import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldType; @@ -132,7 +135,6 @@ import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; import google.registry.monitoring.whitebox.EppMetric; -import google.registry.testing.DatastoreHelper; import google.registry.testing.TaskQueueHelper.TaskMatcher; import java.util.Map; import javax.annotation.Nullable; @@ -1439,6 +1441,20 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase exception) throws Exception { @@ -1579,7 +1595,7 @@ public class DomainCreateFlowTest extends ResourceFlowTestCaseof("irrelevant"))