Add the ability to require premium fee acking for a registrar

When enabled for a registrar, all EPP operations on premium domains that have
costs (e.g.  creates, renews, transfers) will fail unless the EPP fee extension
is used to explicitly ack the amount of fee as part of the EPP transaction.

This ack is required regardless of whether premium fee acking is required at
the registry level. No data migration is necessary since false is the desired
default for this new attribute.

This CL also contains some slight refactoring of static utility methods used to
perform fee verification; there was short-circuiting at call-sites in two
places when what was really needed was two methods, one implementing additional
functionality on top of the other, and calling the inner method in the places
where short-circuiting had previously been necessary.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=184229363
This commit is contained in:
mcilwain 2018-02-01 18:47:22 -08:00 committed by Ben McIlwain
parent 6bcd40f18a
commit 98a61b8181
20 changed files with 264 additions and 119 deletions

View file

@ -433,7 +433,8 @@ public class DomainApplicationCreateFlowTest
}
@Test
public void testFailure_landrushFeeNotProvidedOnPremiumName() throws Exception {
public void testFailure_landrushFeeNotProvidedOnPremiumName_whenRegistryRequiresFeeAcking()
throws Exception {
createTld("example", TldState.SUNRUSH);
setEppInput("domain_create_landrush_premium.xml");
persistContactsAndHosts();
@ -442,6 +443,20 @@ public class DomainApplicationCreateFlowTest
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_landrushFeeNotProvidedOnPremiumName_whenRegistrarRequiresFeeAcking()
throws Exception {
createTld("example", TldState.SUNRUSH);
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_create_landrush_premium.xml");
persistContactsAndHosts();
clock.advanceOneMilli();
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testSuccess_landrushWithClaimsInSunrush() throws Exception {
createTld("tld", TldState.SUNRUSH);

View file

@ -1453,8 +1453,33 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
public void testFailure_premiumNotAcked_byRegistryRequiringAcking() throws Exception {
createTld("example");
assertThat(Registry.get("example").getPremiumPriceAckRequired()).isTrue();
setEppInput("domain_create_premium.xml");
persistContactsAndHosts("net");
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_premiumNotAcked_byRegistrarRequiringAcking() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_create_premium.xml");
persistContactsAndHosts("net");
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_premiumNotAcked_whenRegistrarAndRegistryRequireAcking() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(true).build());
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_create_premium.xml");
persistContactsAndHosts("net");
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);

View file

@ -666,7 +666,7 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
public void testFailure_registryRequiresAcking_feeNotProvidedOnPremiumName() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_renew_premium.xml");
@ -675,6 +675,18 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_registrarRequiresAcking_feeNotProvidedOnPremiumName() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_renew_premium.xml");
persistDomain();
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testIcannActivityReportField_getsLogged() throws Exception {
persistDomain();

View file

@ -559,7 +559,7 @@ public class DomainRestoreRequestFlowTest
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
public void testFailure_premiumNotAcked_whenRegistryRequiresFeeAcking() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
@ -567,6 +567,18 @@ public class DomainRestoreRequestFlowTest
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_premiumNotAcked_whenRegistrarRequiresFeeAcking() throws Exception {
createTld("example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
setEppInput("domain_update_restore_request_premium.xml");
persistPendingDeleteDomain();
EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testIcannActivityReportField_getsLogged() throws Exception {
persistPendingDeleteDomain();

View file

@ -198,64 +198,6 @@ public class DomainTransferRequestFlowTest
.build());
}
/**
* Runs a successful test. The extraExpectedBillingEvents parameter consists of cancellation
* billing event builders that have had all of their attributes set except for the parent history
* entry, which is filled in during the execution of this method.
*/
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
Map<String, String> substitutions,
Optional<Money> transferCost,
BillingEvent.Cancellation.Builder... extraExpectedBillingEvents)
throws Exception {
setEppInput(commandFilename, substitutions);
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
// for the request test we want that same 'now' to be the initial request time, so we shift
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
Registry registry = Registry.get(domain.getTld());
DateTime implicitTransferTime = clock.nowUtc().plus(registry.getAutomaticTransferLength());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(loadFile(expectedXmlFilename, substitutions));
// Transfer should have been requested.
domain = reloadResourceByForeignKey();
// Verify that HistoryEntry was created.
assertAboutDomains()
.that(domain)
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST);
final HistoryEntry historyEntryTransferRequest =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST);
assertAboutHistoryEntries()
.that(historyEntryTransferRequest)
.hasPeriodYears(1)
.and()
.hasOtherClientId("TheRegistrar");
// Verify correct fields were set.
assertTransferRequested(
domain, implicitTransferTime, Period.create(1, Unit.YEARS), expectedExpirationTime);
subordinateHost = reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc());
assertAboutHosts().that(subordinateHost).hasNoHistoryEntries();
assertHistoryEntriesContainBillingEventsAndGracePeriods(
expectedExpirationTime,
implicitTransferTime,
transferCost,
originalGracePeriods,
/* expectTransferBillingEvent = */ true,
extraExpectedBillingEvents);
assertPollMessagesEmitted(expectedExpirationTime, implicitTransferTime);
assertAboutDomainAfterAutomaticTransfer(
expectedExpirationTime, implicitTransferTime, Period.create(1, Unit.YEARS));
}
/** Implements the missing Optional.stream function that is added in Java 9. */
private static <T> Stream<T> optionalToStream(Optional<T> optional) {
return optional.map(Stream::of).orElseGet(Stream::empty);
@ -484,6 +426,64 @@ public class DomainTransferRequestFlowTest
assertThat(afterGracePeriod.getGracePeriods()).isEmpty();
}
/**
* Runs a successful test. The extraExpectedBillingEvents parameter consists of cancellation
* billing event builders that have had all of their attributes set except for the parent history
* entry, which is filled in during the execution of this method.
*/
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
Map<String, String> substitutions,
Optional<Money> transferCost,
BillingEvent.Cancellation.Builder... extraExpectedBillingEvents)
throws Exception {
setEppInput(commandFilename, substitutions);
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
// for the request test we want that same 'now' to be the initial request time, so we shift
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
Registry registry = Registry.get(domain.getTld());
DateTime implicitTransferTime = clock.nowUtc().plus(registry.getAutomaticTransferLength());
// Setup done; run the test.
assertTransactionalFlow(true);
runFlowAssertResponse(loadFile(expectedXmlFilename, substitutions));
// Transfer should have been requested.
domain = reloadResourceByForeignKey();
// Verify that HistoryEntry was created.
assertAboutDomains()
.that(domain)
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST);
final HistoryEntry historyEntryTransferRequest =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST);
assertAboutHistoryEntries()
.that(historyEntryTransferRequest)
.hasPeriodYears(1)
.and()
.hasOtherClientId("TheRegistrar");
// Verify correct fields were set.
assertTransferRequested(
domain, implicitTransferTime, Period.create(1, Unit.YEARS), expectedExpirationTime);
subordinateHost = reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc());
assertAboutHosts().that(subordinateHost).hasNoHistoryEntries();
assertHistoryEntriesContainBillingEventsAndGracePeriods(
expectedExpirationTime,
implicitTransferTime,
transferCost,
originalGracePeriods,
/* expectTransferBillingEvent = */ true,
extraExpectedBillingEvents);
assertPollMessagesEmitted(expectedExpirationTime, implicitTransferTime);
assertAboutDomainAfterAutomaticTransfer(
expectedExpirationTime, implicitTransferTime, Period.create(1, Unit.YEARS));
}
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
@ -1202,7 +1202,7 @@ public class DomainTransferRequestFlowTest
}
@Test
public void testFailure_feeNotProvidedOnPremiumName() throws Exception {
public void testFailure_registryRequiresAcking_feeNotProvidedOnPremiumName() throws Exception {
setupDomain("rich", "example");
EppException thrown =
expectThrows(
@ -1211,6 +1211,20 @@ public class DomainTransferRequestFlowTest
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_registrarRequiresAcking_feeNotProvidedOnPremiumName() throws Exception {
setupDomain("rich", "example");
persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build());
persistResource(
loadRegistrar("NewRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
clock.advanceOneMilli();
EppException thrown =
expectThrows(
FeesRequiredForPremiumNameException.class,
() -> doFailingTest("domain_transfer_request_premium.xml"));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFailure_noAuthInfo() throws Exception {
setupDomain("example", "tld");

View file

@ -97,6 +97,7 @@ public class RegistrarTest extends EntityTestCase {
.setBillingAccountMap(
ImmutableMap.of(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz"))
.setPhonePasscode("01234")
.setPremiumPriceAckRequired(true)
.build());
persistResource(registrar);
abuseAdminContact =

View file

@ -587,6 +587,7 @@ class google.registry.model.registrar.Registrar {
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
boolean blockPremiumNames;
boolean contactsRequireSyncing;
boolean premiumPriceAckRequired;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
google.registry.model.registrar.Registrar$BillingMethod billingMethod;

View file

@ -91,6 +91,7 @@ public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarC
assertThat(registrar.getCreationTime()).isIn(Range.closed(before, after));
assertThat(registrar.getLastUpdateTime()).isEqualTo(registrar.getCreationTime());
assertThat(registrar.getBlockPremiumNames()).isFalse();
assertThat(registrar.getPremiumPriceAckRequired()).isFalse();
verify(connection).send(
eq("/_dr/admin/createGroups"),
@ -766,6 +767,28 @@ public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarC
assertThat(registrar.get().getFaxNumber()).isEqualTo("+1.2125556342");
}
@Test
public void testSuccess_premiumPriceAckRequired() throws Exception {
runCommandForced(
"--name=blobio",
"--password=some_password",
"--registrar_type=REAL",
"--iana_id=8",
"--passcode=01234",
"--icann_referral_email=foo@bar.test",
"--street=\"123 Fake St\"",
"--city Fakington",
"--state MA",
"--zip 00351",
"--cc US",
"--premium_price_ack_required=true",
"clientz");
Optional<Registrar> registrar = Registrar.loadByClientId("clientz");
assertThat(registrar).isPresent();
assertThat(registrar.get().getPremiumPriceAckRequired()).isTrue();
}
@Test
public void testFailure_missingRegistrarType() throws Exception {
IllegalArgumentException thrown =

View file

@ -314,31 +314,55 @@ public class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarC
@Test
public void testSuccess_blockPremiumNames() throws Exception {
assertThat(loadRegistrar("NewRegistrar").getBlockPremiumNames()).isFalse();
runCommand("--block_premium=true", "--force", "NewRegistrar");
runCommandForced("--block_premium=true", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getBlockPremiumNames()).isTrue();
}
@Test
public void testSuccess_resetBlockPremiumNames() throws Exception {
persistResource(loadRegistrar("NewRegistrar").asBuilder().setBlockPremiumNames(true).build());
runCommand("--block_premium=false", "--force", "NewRegistrar");
runCommandForced("--block_premium=false", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getBlockPremiumNames()).isFalse();
}
@Test
public void testSuccess_blockPremiumNamesUnspecified() throws Exception {
persistResource(loadRegistrar("NewRegistrar").asBuilder().setBlockPremiumNames(true).build());
// Make some unrelated change where we don't specify "--block_premium".
runCommand("--billing_id=12345", "--force", "NewRegistrar");
// Make sure the field didn't get reset back to false.
assertThat(loadRegistrar("NewRegistrar").getBlockPremiumNames()).isTrue();
public void testSuccess_premiumPriceAckRequired() throws Exception {
assertThat(loadRegistrar("NewRegistrar").getPremiumPriceAckRequired()).isFalse();
runCommandForced("--premium_price_ack_required=true", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getPremiumPriceAckRequired()).isTrue();
}
@Test
public void testSuccess_resetPremiumPriceAckRequired() throws Exception {
persistResource(
loadRegistrar("NewRegistrar").asBuilder().setPremiumPriceAckRequired(true).build());
runCommandForced("--premium_price_ack_required=false", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getPremiumPriceAckRequired()).isFalse();
}
@Test
public void testSuccess_unspecifiedBooleansArentChanged() throws Exception {
persistResource(
loadRegistrar("NewRegistrar")
.asBuilder()
.setBlockPremiumNames(true)
.setPremiumPriceAckRequired(true)
.setContactsRequireSyncing(true)
.build());
// Make some unrelated change where we don't specify the flags for the booleans.
runCommandForced("--billing_id=12345", "NewRegistrar");
// Make sure that the boolean fields didn't get reset back to false.
Registrar reloadedRegistrar = loadRegistrar("NewRegistrar");
assertThat(reloadedRegistrar.getBlockPremiumNames()).isTrue();
assertThat(reloadedRegistrar.getPremiumPriceAckRequired()).isTrue();
assertThat(reloadedRegistrar.getContactsRequireSyncing()).isTrue();
}
@Test
public void testSuccess_updateMultiple() throws Exception {
assertThat(loadRegistrar("TheRegistrar").getState()).isEqualTo(State.ACTIVE);
assertThat(loadRegistrar("NewRegistrar").getState()).isEqualTo(State.ACTIVE);
runCommand("--registrar_state=SUSPENDED", "--force", "TheRegistrar", "NewRegistrar");
runCommandForced("--registrar_state=SUSPENDED", "TheRegistrar", "NewRegistrar");
assertThat(loadRegistrar("TheRegistrar").getState()).isEqualTo(State.SUSPENDED);
assertThat(loadRegistrar("NewRegistrar").getState()).isEqualTo(State.SUSPENDED);
}