Return all applicable reserved list entries associated with a label

Instead of only returning the most severe one, return all applicable ones. This is because the reserved list has grown to a list of types that are not strictly comparable but orthogonal to each other. We can no longer depend on the fact that the most severe type incorporates all properties of those beneath it. Therefore returning all of them and treat them one by one in the calling site is the correct behavior.

Due to constraint imposed in eppcom.xsd, during domain checks the response can only contain a reservation reason of fewer than 32 characters, therefore we are returning the message for the type with highest severity, in case of multiple reservation types for a label.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149776106
This commit is contained in:
jianglai 2017-03-10 11:05:02 -08:00 committed by Ben McIlwain
parent 9a11f125ff
commit ebcdae7361
8 changed files with 199 additions and 86 deletions

View file

@ -21,7 +21,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse; import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
import static google.registry.flows.domain.DomainFlowUtils.failfastForCreate; import static google.registry.flows.domain.DomainFlowUtils.failfastForCreate;
import static google.registry.flows.domain.DomainFlowUtils.getReservationType; import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpTokenEntity; import static google.registry.flows.domain.DomainFlowUtils.prepareMarkedLrpTokenEntity;
import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers; import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName; import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
@ -85,6 +85,7 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.label.ReservationType; import google.registry.model.registry.label.ReservationType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.tmch.LordnTask; import google.registry.tmch.LordnTask;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -169,7 +170,7 @@ public class DomainAllocateFlow implements TransactionalFlow {
.addGracePeriod(createGracePeriod( .addGracePeriod(createGracePeriod(
isSunrushAddGracePeriod, getOnly(billsAndPolls, BillingEvent.OneTime.class))) isSunrushAddGracePeriod, getOnly(billsAndPolls, BillingEvent.OneTime.class)))
// Names on the collision list will not be delegated. Set server hold. // Names on the collision list will not be delegated. Set server hold.
.setStatusValues(ReservationType.NAME_COLLISION == getReservationType(domainName) .setStatusValues(getReservationTypes(domainName).contains(ReservationType.NAME_COLLISION)
? ImmutableSet.of(StatusValue.SERVER_HOLD) ? ImmutableSet.of(StatusValue.SERVER_HOLD)
: ImmutableSet.<StatusValue>of()) : ImmutableSet.<StatusValue>of())
.setRegistrant(command.getRegistrant()) .setRegistrant(command.getRegistrant())
@ -241,7 +242,7 @@ public class DomainAllocateFlow implements TransactionalFlow {
BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent( BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent(
application, historyEntry, isSunrushAddGracePeriod, registry, now, years); application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
PollMessage.OneTime oneTimePollMessage = PollMessage.OneTime oneTimePollMessage =
createOneTimePollMessage(application, historyEntry, getReservationType(domainName), now); createOneTimePollMessage(application, historyEntry, getReservationTypes(domainName), now);
// Create a new autorenew billing event and poll message starting at the expiration time. // Create a new autorenew billing event and poll message starting at the expiration time.
BillingEvent.Recurring autorenewBillingEvent = BillingEvent.Recurring autorenewBillingEvent =
createAutorenewBillingEvent(historyEntry, registrationExpirationTime); createAutorenewBillingEvent(historyEntry, registrationExpirationTime);
@ -336,18 +337,21 @@ public class DomainAllocateFlow implements TransactionalFlow {
private PollMessage.OneTime createOneTimePollMessage( private PollMessage.OneTime createOneTimePollMessage(
DomainApplication application, DomainApplication application,
HistoryEntry historyEntry, HistoryEntry historyEntry,
ReservationType reservationType, Set<ReservationType> reservationTypes,
DateTime now) { DateTime now) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setClientId(historyEntry.getClientId()) .setClientId(historyEntry.getClientId())
.setEventTime(now) .setEventTime(now)
.setMsg(reservationType == ReservationType.NAME_COLLISION .setMsg(
reservationTypes.contains(ReservationType.NAME_COLLISION)
? COLLISION_MESSAGE // Remind the registrar of the name collision policy. ? COLLISION_MESSAGE // Remind the registrar of the name collision policy.
: "Domain was allocated") : "Domain was allocated")
.setResponseData(ImmutableList.of( .setResponseData(
ImmutableList.of(
DomainPendingActionNotificationResponse.create( DomainPendingActionNotificationResponse.create(
targetId, true, application.getCreationTrid(), now))) targetId, true, application.getCreationTrid(), now)))
.setResponseExtensions(ImmutableList.of( .setResponseExtensions(
ImmutableList.of(
new LaunchInfoResponseExtension.Builder() new LaunchInfoResponseExtension.Builder()
.setApplicationId(application.getForeignKey()) .setApplicationId(application.getForeignKey())
.setPhase(application.getPhase()) .setPhase(application.getPhase())

View file

@ -17,7 +17,7 @@ package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.getReservationType; import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
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;
@ -25,6 +25,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegat
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName; import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
import static google.registry.model.registry.label.ReservationType.UNRESERVED; import static google.registry.model.registry.label.ReservationType.UNRESERVED;
import static google.registry.model.registry.label.ReservationType.getTypeOfHighestSeverity;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -179,14 +180,15 @@ public final class DomainCheckFlow implements Flow {
}})) { }})) {
return "Pending allocation"; return "Pending allocation";
} }
ReservationType reservationType = getReservationType(domainName); ImmutableSet<ReservationType> reservationTypes = getReservationTypes(domainName);
if (reservationType == UNRESERVED if (reservationTypes.equals(ImmutableSet.of(UNRESERVED))
&& isDomainPremium(domainName.toString(), now) && isDomainPremium(domainName.toString(), now)
&& registry.getPremiumPriceAckRequired() && registry.getPremiumPriceAckRequired()
&& eppInput.getSingleExtension(FeeCheckCommandExtension.class) == null) { && eppInput.getSingleExtension(FeeCheckCommandExtension.class) == null) {
return "Premium names require EPP ext."; return "Premium names require EPP ext.";
} }
return reservationType.getMessageForCheck();
return getTypeOfHighestSeverity(reservationTypes).getMessageForCheck();
} }
/** Handle the fee check extension. */ /** Handle the fee check extension. */

View file

@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.equalTo;
import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union; import static com.google.common.collect.Sets.union;
import static google.registry.flows.domain.DomainPricingLogic.getMatchingLrpToken; import static google.registry.flows.domain.DomainPricingLogic.getMatchingLrpToken;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
@ -26,7 +27,6 @@ import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName; import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.label.ReservedList.getReservation;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.tldconfig.idn.IdnLabelValidator.findValidIdnTableForTld; import static google.registry.tldconfig.idn.IdnLabelValidator.findValidIdnTableForTld;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
@ -102,6 +102,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservationType; import google.registry.model.registry.label.ReservationType;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tmch.ClaimsListShard; import google.registry.model.tmch.ClaimsListShard;
import google.registry.util.Idn; import google.registry.util.Idn;
@ -351,16 +352,17 @@ public class DomainFlowUtils {
} }
private static boolean isReserved(InternetDomainName domainName, boolean isSunrise) { private static boolean isReserved(InternetDomainName domainName, boolean isSunrise) {
ReservationType type = getReservationType(domainName); ImmutableSet<ReservationType> types = getReservationTypes(domainName);
return type == ReservationType.FULLY_BLOCKED return types.contains(ReservationType.FULLY_BLOCKED)
|| type == ReservationType.RESERVED_FOR_ANCHOR_TENANT || types.contains(ReservationType.RESERVED_FOR_ANCHOR_TENANT)
|| (TYPES_ALLOWED_FOR_CREATE_ONLY_IN_SUNRISE.contains(type) && !isSunrise); || !(isSunrise || intersection(TYPES_ALLOWED_FOR_CREATE_ONLY_IN_SUNRISE, types).isEmpty());
} }
/** Returns an enum that encodes how and when this name is reserved in the current tld. */ /** Returns a set of {@link ReservationType}s for the given domain name. */
static ReservationType getReservationType(InternetDomainName domainName) { static ImmutableSet<ReservationType> getReservationTypes(InternetDomainName domainName) {
// The TLD should always be the parent of the requested domain name. // The TLD should always be the parent of the requested domain name.
return getReservation(domainName.parts().get(0), domainName.parent().toString()); return ReservedList.getReservationTypes(
domainName.parts().get(0), domainName.parent().toString());
} }
/** Verifies that a launch extension's specified phase matches the specified registry's phase. */ /** Verifies that a launch extension's specified phase matches the specified registry's phase. */

View file

@ -19,7 +19,7 @@ import static com.google.common.base.Strings.emptyToNull;
import static google.registry.model.registry.Registry.TldState.SUNRISE; import static google.registry.model.registry.Registry.TldState.SUNRISE;
import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice; import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice;
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION; import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
import static google.registry.model.registry.label.ReservedList.getReservation; import static google.registry.model.registry.label.ReservedList.getReservationTypes;
import static google.registry.util.DomainNameUtils.getTldFromDomainName; import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@ -46,7 +46,7 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
Optional<Money> premiumPrice = getPremiumPrice(label, registry); Optional<Money> premiumPrice = getPremiumPrice(label, registry);
boolean isNameCollisionInSunrise = boolean isNameCollisionInSunrise =
registry.getTldState(priceTime).equals(SUNRISE) registry.getTldState(priceTime).equals(SUNRISE)
&& getReservation(label, tld) == NAME_COLLISION; && getReservationTypes(label, tld).contains(NAME_COLLISION);
String feeClass = emptyToNull(Joiner.on('-').skipNulls().join( String feeClass = emptyToNull(Joiner.on('-').skipNulls().join(
premiumPrice.isPresent() ? "premium" : null, premiumPrice.isPresent() ? "premium" : null,
isNameCollisionInSunrise ? "collision" : null)); isNameCollisionInSunrise ? "collision" : null));

View file

@ -16,13 +16,16 @@ package google.registry.model.registry.label;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** Enum describing reservation on a label in a {@link ReservedList} */ /** Enum describing reservation on a label in a {@link ReservedList} */
public enum ReservationType { public enum ReservationType {
// We explicitly set the severity, even though we have a checkState that makes it equal to the // We explicitly set the severity, even though we have a checkState that makes it equal to the
// ordinal, so that no one accidentally reorders these values and changes the sort order. // ordinal, so that no one accidentally reorders these values and changes the sort order. If a
// label has multiple reservation types, its message is the that of the one with the highest
// severity.
UNRESERVED(null, 0), UNRESERVED(null, 0),
ALLOWED_IN_SUNRISE("Reserved for non-sunrise", 1), ALLOWED_IN_SUNRISE("Reserved for non-sunrise", 1),
@ -43,4 +46,21 @@ public enum ReservationType {
public String getMessageForCheck() { public String getMessageForCheck() {
return messageForCheck; return messageForCheck;
} }
/**
* Returns the {@code ReservationType} with the highest severity, used when a label has multiple
* reservation types and a reservation message is needed.
* @param types the set of reservation types that a label is associated with.
* @return the reservation type with the highest severity.
*/
public static ReservationType getTypeOfHighestSeverity(Set<ReservationType> types) {
ReservationType mostSevereType = UNRESERVED;
for (ReservationType type : types) {
if (type.compareTo(mostSevereType) > 0) {
mostSevereType = type;
}
}
return mostSevereType;
}
} }

View file

@ -16,6 +16,8 @@ package google.registry.model.registry.label;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static google.registry.config.RegistryConfig.getDomainLabelListCacheDuration; import static google.registry.config.RegistryConfig.getDomainLabelListCacheDuration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
@ -26,6 +28,7 @@ import static google.registry.model.registry.label.ReservationType.UNRESERVED;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.base.Function;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
@ -33,6 +36,7 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.UncheckedExecutionException;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
@ -42,9 +46,10 @@ import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Mapify; import com.googlecode.objectify.annotation.Mapify;
import com.googlecode.objectify.mapper.Mapper; import com.googlecode.objectify.mapper.Mapper;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -72,9 +77,8 @@ public final class ReservedList
ReservationType reservationType; ReservationType reservationType;
/** /**
* Contains the auth code necessary to register a domain with this label. * Contains the auth code necessary to register a domain with this label. Note that this field
* Note that this field will only ever be populated for entries with type * will only ever be populated for entries with type RESERVED_FOR_ANCHOR_TENANT.
* RESERVED_FOR_ANCHOR_TENANT.
*/ */
String authCode; String authCode;
@ -141,8 +145,8 @@ public final class ReservedList
/** /**
* Gets a ReservedList by name using the caching layer. * Gets a ReservedList by name using the caching layer.
* *
* @return An Optional<ReservedList> that has a value if a reserved list exists by the given * @return An Optional<ReservedList> that has a value if a reserved list exists by the given name,
* name, or absent if not. * or absent if not.
* @throws UncheckedExecutionException if some other error occurs while trying to load the * @throws UncheckedExecutionException if some other error occurs while trying to load the
* ReservedList from the cache or Datastore. * ReservedList from the cache or Datastore.
*/ */
@ -157,53 +161,69 @@ public final class ReservedList
/** /**
* Queries the set of all reserved lists associated with the specified tld and returns the * Queries the set of all reserved lists associated with the specified tld and returns the
* reservation type of the first occurrence of label seen. If the label is in none of the lists, * reservation types of the label. If the label is in none of the lists, it returns a set that
* it returns UNRESERVED. * contains UNRESERVED.
*/ */
public static ReservationType getReservation(String label, String tld) { public static ImmutableSet<ReservationType> getReservationTypes(String label, String tld) {
checkNotNull(label, "label"); checkNotNull(label, "label");
if (label.length() == 0) { if (label.length() == 0) {
return FULLY_BLOCKED; return ImmutableSet.of(FULLY_BLOCKED);
} }
ReservedListEntry entry = getReservedListEntry(label, tld); ImmutableSet<ReservedListEntry> entries = getReservedListEntries(label, tld);
return (entry != null) ? entry.reservationType : UNRESERVED; return entries.isEmpty()
? ImmutableSet.of(UNRESERVED)
: ImmutableSet.copyOf(
Iterables.transform(
entries,
new Function<ReservedListEntry, ReservationType>() {
@Override
public ReservationType apply(ReservedListEntry reservedListEntry) {
return reservedListEntry.reservationType;
}
}));
} }
/** /**
* Returns true if the given label and TLD is reserved for an anchor tenant, and the given * Returns true if the given label and TLD is reserved for an anchor tenant, and the given auth
* auth code matches the one set on the reservation. * code matches the one set on the reservation. If there are multiple anchor tenant entries fo
* this label, all the auth codes need to be the same and match the given one, otherwise an
* exception is thrown.
*/ */
public static boolean matchesAnchorTenantReservation( public static boolean matchesAnchorTenantReservation(
InternetDomainName domainName, String authCode) { InternetDomainName domainName, String authCode) {
ReservedListEntry entry = ImmutableSet<ReservedListEntry> entries =
getReservedListEntry(domainName.parts().get(0), domainName.parent().toString()); getReservedListEntries(domainName.parts().get(0), domainName.parent().toString());
return entry != null
&& entry.reservationType == RESERVED_FOR_ANCHOR_TENANT Set<String> domainAuthCodes = new HashSet<>();
&& Objects.equals(entry.getAuthCode(), authCode); for (ReservedListEntry entry : entries) {
if (entry.reservationType == RESERVED_FOR_ANCHOR_TENANT) {
domainAuthCodes.add(entry.getAuthCode());
}
}
checkState(
domainAuthCodes.size() <= 1, "There are conflicting auth codes for domain: %s", domainName);
return !domainAuthCodes.isEmpty() && getOnlyElement(domainAuthCodes).equals(authCode);
} }
/** /**
* Helper function to retrieve the entry associated with this label and TLD, or null if no such * Helper function to retrieve the entries associated with this label and TLD, or an empty set if
* entry exists. * no such entry exists.
*/ */
@Nullable private static ImmutableSet<ReservedListEntry> getReservedListEntries(String label, String tld) {
private static ReservedListEntry getReservedListEntry(String label, String tld) {
Registry registry = Registry.get(checkNotNull(tld, "tld")); Registry registry = Registry.get(checkNotNull(tld, "tld"));
ImmutableSet<Key<ReservedList>> reservedLists = registry.getReservedLists(); ImmutableSet<Key<ReservedList>> reservedLists = registry.getReservedLists();
ImmutableSet<ReservedList> lists = loadReservedLists(reservedLists); ImmutableSet<ReservedList> lists = loadReservedLists(reservedLists);
ReservedListEntry entry = null; ImmutableSet.Builder<ReservedListEntry> entries = new ImmutableSet.Builder<>();
// Loop through all reservation lists and check each one for the inputted label, and return // Loop through all reservation lists and add each of them.
// the most severe ReservationType found.
for (ReservedList rl : lists) { for (ReservedList rl : lists) {
Map<String, ReservedListEntry> entries = rl.getReservedListEntries(); if (rl.getReservedListEntries().containsKey(label)) {
ReservedListEntry nextEntry = entries.get(label); entries.add(rl.getReservedListEntries().get(label));
if (nextEntry != null
&& (entry == null || nextEntry.reservationType.compareTo(entry.reservationType) > 0)) {
entry = nextEntry;
} }
} }
return entry;
return entries.build();
} }
private static ImmutableSet<ReservedList> loadReservedLists( private static ImmutableSet<ReservedList> loadReservedLists(

View file

@ -115,6 +115,24 @@ public class DomainCheckFlowTest
create(true, "example3.tld", null)); create(true, "example3.tld", null));
} }
@Test
public void testSuccess_domainWithMultipleReservationType_useMostSevereMessage()
throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(
createReservedList(),
persistReservedList("tld-collision", "allowedinsunrise,NAME_COLLISION"))
.build());
setEppInput("domain_check_one_tld_reserved.xml");
doCheckTest(
create(false, "reserved.tld", "Reserved"),
create(false, "allowedinsunrise.tld", "Cannot be delegated"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test @Test
public void testSuccess_anchorTenantReserved() throws Exception { public void testSuccess_anchorTenantReserved() throws Exception {
setEppInput("domain_check_anchor.xml"); setEppInput("domain_check_anchor.xml");
@ -398,6 +416,19 @@ public class DomainCheckFlowTest
runFlowAssertResponse(readFile("domain_check_fee_response_v06.xml")); runFlowAssertResponse(readFile("domain_check_fee_response_v06.xml"));
} }
@Test
public void testFeeExtension_multipleReservations() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(
persistReservedList("example-sunrise", "allowedinsunrise,ALLOWED_IN_SUNRISE"))
.build());
persistActiveDomain("example1.tld");
setEppInput("domain_check_fee_v06.xml");
runFlowAssertResponse(readFile("domain_check_fee_response_v06.xml"));
}
@Test @Test
public void testFeeExtension_v11() throws Exception { public void testFeeExtension_v11() throws Exception {
persistActiveDomain("example1.tld"); persistActiveDomain("example1.tld");

View file

@ -18,9 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE; import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED; import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT; import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
import static google.registry.model.registry.label.ReservationType.UNRESERVED; import static google.registry.model.registry.label.ReservationType.UNRESERVED;
import static google.registry.model.registry.label.ReservedList.getReservation; import static google.registry.model.registry.label.ReservedList.getReservationTypes;
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation; import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistReservedList; import static google.registry.testing.DatastoreHelper.persistReservedList;
@ -71,20 +72,20 @@ public class ReservedListTest {
@Test @Test
public void testGetReservation_allLabelsAreUnreserved_withNoReservedLists() throws Exception { public void testGetReservation_allLabelsAreUnreserved_withNoReservedLists() throws Exception {
assertThat(Registry.get("tld").getReservedLists()).isEmpty(); assertThat(Registry.get("tld").getReservedLists()).isEmpty();
assertThat(getReservation("doodle", "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("doodle", "tld")).containsExactly(UNRESERVED);
assertThat(getReservation("access", "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("access", "tld")).containsExactly(UNRESERVED);
assertThat(getReservation("rich", "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("rich", "tld")).containsExactly(UNRESERVED);
} }
@Test @Test
public void testZeroReservedLists_doesNotCauseError() throws Exception { public void testZeroReservedLists_doesNotCauseError() throws Exception {
assertThat(getReservation("doodle", "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("doodle", "tld")).containsExactly(UNRESERVED);
} }
@Test @Test
public void testGetReservation_twoLetterCodesAreAvailable() { public void testGetReservation_twoLetterCodesAreAvailable() {
for (String sld : ImmutableList.of("aa", "az", "zz", "91", "1n", "j5")) { for (String sld : ImmutableList.of("aa", "az", "zz", "91", "1n", "j5")) {
assertThat(getReservation(sld, "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes(sld, "tld")).containsExactly(UNRESERVED);
} }
} }
@ -92,7 +93,7 @@ public class ReservedListTest {
public void testGetReservation_singleCharacterDomainsAreAllowed() { public void testGetReservation_singleCharacterDomainsAreAllowed() {
// This isn't quite exhaustive but it's close. // This isn't quite exhaustive but it's close.
for (char c = 'a'; c <= 'z'; c++) { for (char c = 'a'; c <= 'z'; c++) {
assertThat(getReservation("" + c, "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("" + c, "tld")).containsExactly(UNRESERVED);
} }
} }
@ -107,8 +108,8 @@ public class ReservedListTest {
"lol,RESERVED_FOR_ANCHOR_TENANT,foobar1", "lol,RESERVED_FOR_ANCHOR_TENANT,foobar1",
"lol2,RESERVED_FOR_ANCHOR_TENANT,abcdefg # This is a comment"))) "lol2,RESERVED_FOR_ANCHOR_TENANT,abcdefg # This is a comment")))
.build()); .build());
assertThat(getReservation("lol", "tld")).isEqualTo(RESERVED_FOR_ANCHOR_TENANT); assertThat(getReservationTypes("lol", "tld")).containsExactly(RESERVED_FOR_ANCHOR_TENANT);
assertThat(getReservation("lol2", "tld")).isEqualTo(RESERVED_FOR_ANCHOR_TENANT); assertThat(getReservationTypes("lol2", "tld")).containsExactly(RESERVED_FOR_ANCHOR_TENANT);
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar1")) assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar1"))
.isTrue(); .isTrue();
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar")) assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foobar"))
@ -139,6 +140,21 @@ public class ReservedListTest {
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol5.tld"), "")).isFalse(); assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol5.tld"), "")).isFalse();
} }
@Test
public void testMatchesAnchorTenantReservation_duplicatingAuthCodes()
throws Exception {
ReservedList rl1 = persistReservedList("reserved1", "lol,RESERVED_FOR_ANCHOR_TENANT,foo");
ReservedList rl2 = persistReservedList("reserved2", "lol,RESERVED_FOR_ANCHOR_TENANT,foo");
createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "foo")).isTrue();
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "bar")).isFalse();
persistReservedList("reserved2", "lol,RESERVED_FOR_ANCHOR_TENANT,bar");
thrown.expect(
IllegalStateException.class, "There are conflicting auth codes for domain: lol.tld");
matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "bar");
}
@Test @Test
public void testGetReservation_concatsMultipleListsCorrectly() throws Exception { public void testGetReservation_concatsMultipleListsCorrectly() throws Exception {
ReservedList rl1 = persistReservedList( ReservedList rl1 = persistReservedList(
@ -152,13 +168,30 @@ public class ReservedListTest {
createTld("tld"); createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build()); persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
assertThat(getReservation("lol", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("lol", "tld")).containsExactly(FULLY_BLOCKED);
assertThat(getReservation("cat", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("cat", "tld")).containsExactly(FULLY_BLOCKED);
assertThat(getReservation("roflcopter", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("roflcopter", "tld")).containsExactly(FULLY_BLOCKED);
assertThat(getReservation("snowcrash", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("snowcrash", "tld")).containsExactly(FULLY_BLOCKED);
assertThat(getReservation("doge", "tld")).isEqualTo(UNRESERVED); assertThat(getReservationTypes("doge", "tld")).containsExactly(UNRESERVED);
} }
@Test
public void testGetReservation_returnsAllReservationTypesFromMultipleListsForTheSameLabel()
throws Exception {
ReservedList rl1 =
persistReservedList("reserved1", "lol,NAME_COLLISION # yup", "cat,FULLY_BLOCKED");
ReservedList rl2 =
persistReservedList("reserved2", "lol,ALLOWED_IN_SUNRISE", "snowcrash,FULLY_BLOCKED");
createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
assertThat(getReservationTypes("lol", "tld"))
.containsExactly(NAME_COLLISION, ALLOWED_IN_SUNRISE);
assertThat(getReservationTypes("cat", "tld")).containsExactly(FULLY_BLOCKED);
assertThat(getReservationTypes("snowcrash", "tld")).containsExactly(FULLY_BLOCKED);
}
@Test @Test
public void testGetReservation_worksAfterReservedListRemovedUsingSet() throws Exception { public void testGetReservation_worksAfterReservedListRemovedUsingSet() throws Exception {
ReservedList rl1 = persistReservedList( ReservedList rl1 = persistReservedList(
@ -167,23 +200,24 @@ public class ReservedListTest {
"reserved2", "roflcopter,FULLY_BLOCKED", "snowcrash,FULLY_BLOCKED"); "reserved2", "roflcopter,FULLY_BLOCKED", "snowcrash,FULLY_BLOCKED");
createTld("tld"); createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build()); persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
assertThat(getReservation("roflcopter", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("roflcopter", "tld")).containsExactly(FULLY_BLOCKED);
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1).build()); persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1).build());
assertWithMessage( assertWithMessage(
"roflcopter.tld should be unreserved after unsetting the registry's second reserved list") "roflcopter.tld should be unreserved"
.that(getReservation("roflcopter", "tld")) + " after unsetting the registry's second reserved list")
.isEqualTo(UNRESERVED); .that(getReservationTypes("roflcopter", "tld"))
.containsExactly(UNRESERVED);
} }
@Test @Test
public void testGetReservation_combinesMultipleLists_handlesSeverityCorrectly() throws Exception { public void testGetReservation_combinesMultipleLists() throws Exception {
ReservedList rl1 = persistReservedList( ReservedList rl1 = persistReservedList(
"reserved1", "lol,NAME_COLLISION", "roflcopter,ALLOWED_IN_SUNRISE"); "reserved1", "lol,NAME_COLLISION", "roflcopter,ALLOWED_IN_SUNRISE");
ReservedList rl2 = persistReservedList("reserved2", "lol,FULLY_BLOCKED"); ReservedList rl2 = persistReservedList("reserved2", "lol,FULLY_BLOCKED");
createTld("tld"); createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build()); persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
assertThat(getReservation("lol", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("lol", "tld")).containsExactly(FULLY_BLOCKED, NAME_COLLISION);
assertThat(getReservation("roflcopter", "tld")).isEqualTo(ALLOWED_IN_SUNRISE); assertThat(getReservationTypes("roflcopter", "tld")).containsExactly(ALLOWED_IN_SUNRISE);
} }
@Test @Test
@ -191,7 +225,7 @@ public class ReservedListTest {
ReservedList rl = persistReservedList("tld-reserved", "lol,FULLY_BLOCKED # yup"); ReservedList rl = persistReservedList("tld-reserved", "lol,FULLY_BLOCKED # yup");
createTld("tld"); createTld("tld");
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl).build()); persistResource(Registry.get("tld").asBuilder().setReservedLists(rl).build());
assertThat(getReservation("lol", "tld")).isEqualTo(FULLY_BLOCKED); assertThat(getReservationTypes("lol", "tld")).containsExactly(FULLY_BLOCKED);
} }
@Test @Test