mirror of
https://github.com/google/nomulus.git
synced 2025-07-22 18:55:58 +02:00
Add REGISTER_BSA allocation type (#2319)
* Add ALLOW_BSA allocation type Add a new type to allow creation of domains blocked by BSA. Except for the BSA semantics, the new type behaves exactly like SINGLE_USE. * Addressing reviews * Addressing review
This commit is contained in:
parent
469d62703a
commit
7b47ecb1f1
11 changed files with 239 additions and 33 deletions
|
@ -26,6 +26,7 @@ import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccoun
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
|
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.isAnchorTenant;
|
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
|
||||||
|
import static google.registry.flows.domain.DomainFlowUtils.isRegisterBsaCreate;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
|
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||||
|
@ -269,13 +270,13 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
if (tokenResult.isPresent()) {
|
if (tokenResult.isPresent()) {
|
||||||
return tokenResult;
|
return tokenResult;
|
||||||
}
|
}
|
||||||
if (bsaBlockedDomains.contains(domainName)) {
|
if (isRegisterBsaCreate(domainName, allocationToken)
|
||||||
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
|
|| !bsaBlockedDomains.contains(domainName)) {
|
||||||
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
|
|
||||||
return Optional.of("Blocked by a GlobalBlock service");
|
|
||||||
} else {
|
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
|
||||||
|
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
|
||||||
|
return Optional.of("Blocked by a GlobalBlock service");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle the fee check extension. */
|
/** Handle the fee check extension. */
|
||||||
|
|
|
@ -330,7 +330,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||||
.verifySignedMarks(launchCreate.get().getSignedMarks(), domainLabel, now)
|
.verifySignedMarks(launchCreate.get().getSignedMarks(), domainLabel, now)
|
||||||
.getId();
|
.getId();
|
||||||
}
|
}
|
||||||
verifyNotBlockedByBsa(domainLabel, tld, now);
|
verifyNotBlockedByBsa(domainName, tld, now, allocationToken);
|
||||||
flowCustomLogic.afterValidation(
|
flowCustomLogic.afterValidation(
|
||||||
DomainCreateFlowCustomLogic.AfterValidationParameters.newBuilder()
|
DomainCreateFlowCustomLogic.AfterValidationParameters.newBuilder()
|
||||||
.setDomainName(domainName)
|
.setDomainName(domainName)
|
||||||
|
@ -421,8 +421,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||||
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
|
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
|
||||||
}
|
}
|
||||||
entitiesToSave.add(domain, domainHistory);
|
entitiesToSave.add(domain, domainHistory);
|
||||||
if (allocationToken.isPresent()
|
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
|
||||||
entitiesToSave.add(
|
entitiesToSave.add(
|
||||||
allocationTokenFlowUtils.redeemToken(
|
allocationTokenFlowUtils.redeemToken(
|
||||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||||
|
|
|
@ -27,6 +27,7 @@ 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.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
||||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||||
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
||||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||||
|
@ -265,9 +266,14 @@ public class DomainFlowUtils {
|
||||||
* Verifies that the {@code domainLabel} is not blocked by any BSA block label for the given
|
* Verifies that the {@code domainLabel} is not blocked by any BSA block label for the given
|
||||||
* {@code tld} at the specified time.
|
* {@code tld} at the specified time.
|
||||||
*/
|
*/
|
||||||
public static void verifyNotBlockedByBsa(String domainLabel, Tld tld, DateTime now)
|
public static void verifyNotBlockedByBsa(
|
||||||
|
InternetDomainName domainName,
|
||||||
|
Tld tld,
|
||||||
|
DateTime now,
|
||||||
|
Optional<AllocationToken> allocationToken)
|
||||||
throws DomainLabelBlockedByBsaException {
|
throws DomainLabelBlockedByBsaException {
|
||||||
if (isBlockedByBsa(domainLabel, tld, now)) {
|
if (!isRegisterBsaCreate(domainName, allocationToken)
|
||||||
|
&& isBlockedByBsa(domainName.parts().get(0), tld, now)) {
|
||||||
throw new DomainLabelBlockedByBsaException();
|
throw new DomainLabelBlockedByBsaException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,6 +317,15 @@ public class DomainFlowUtils {
|
||||||
&& token.get().getDomainName().get().equals(domainName.toString());
|
&& token.get().getDomainName().get().equals(domainName.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether a given domain create request may bypass the BSA block check. */
|
||||||
|
public static boolean isRegisterBsaCreate(
|
||||||
|
InternetDomainName domainName, Optional<AllocationToken> token) {
|
||||||
|
return token.isPresent()
|
||||||
|
&& token.get().getTokenType().equals(REGISTER_BSA)
|
||||||
|
&& token.get().getDomainName().isPresent()
|
||||||
|
&& token.get().getDomainName().get().equals(domainName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if the registrar running the flow has access to the TLD in question. */
|
/** Check if the registrar running the flow has access to the TLD in question. */
|
||||||
public static void checkAllowedAccessToTld(String registrarId, String tld) throws EppException {
|
public static void checkAllowedAccessToTld(String registrarId, String tld) throws EppException {
|
||||||
if (!Registrar.loadByRegistrarIdCached(registrarId).get().getAllowedTlds().contains(tld)) {
|
if (!Registrar.loadByRegistrarIdCached(registrarId).get().getAllowedTlds().contains(tld)) {
|
||||||
|
|
|
@ -73,7 +73,6 @@ import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
|
||||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.eppcommon.AuthInfo;
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
@ -258,8 +257,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||||
entitiesToSave.add(
|
entitiesToSave.add(
|
||||||
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||||
if (allocationToken.isPresent()
|
if (allocationToken.isPresent() && allocationToken.get().getTokenType().isOneTimeUse()) {
|
||||||
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
|
|
||||||
entitiesToSave.add(
|
entitiesToSave.add(
|
||||||
allocationTokenFlowUtils.redeemToken(
|
allocationTokenFlowUtils.redeemToken(
|
||||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||||
|
|
|
@ -36,7 +36,6 @@ import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
|
||||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||||
import google.registry.model.tld.Tld;
|
import google.registry.model.tld.Tld;
|
||||||
|
@ -105,8 +104,7 @@ public class AllocationTokenFlowUtils {
|
||||||
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
||||||
public AllocationToken redeemToken(AllocationToken token, HistoryEntryId redemptionHistoryId) {
|
public AllocationToken redeemToken(AllocationToken token, HistoryEntryId redemptionHistoryId) {
|
||||||
checkArgument(
|
checkArgument(
|
||||||
TokenType.SINGLE_USE.equals(token.getTokenType()),
|
token.getTokenType().isOneTimeUse(), "Only SINGLE_USE tokens can be marked as redeemed");
|
||||||
"Only SINGLE_USE tokens can be marked as redeemed");
|
|
||||||
return token.asBuilder().setRedemptionHistoryId(redemptionHistoryId).build();
|
return token.asBuilder().setRedemptionHistoryId(redemptionHistoryId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.CAN
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||||
|
@ -120,18 +121,37 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||||
/** Type of the token that indicates how and where it should be used. */
|
/** Type of the token that indicates how and where it should be used. */
|
||||||
public enum TokenType {
|
public enum TokenType {
|
||||||
/** Token used for bulk pricing */
|
/** Token used for bulk pricing */
|
||||||
BULK_PRICING,
|
BULK_PRICING(/* isOneTimeUse= */ false),
|
||||||
/** Token saved on a TLD to use if no other token is passed from the client */
|
/** Token saved on a TLD to use if no other token is passed from the client */
|
||||||
DEFAULT_PROMO,
|
DEFAULT_PROMO(/* isOneTimeUse= */ false),
|
||||||
/** This is the old name for what is now BULK_PRICING. */
|
/** This is the old name for what is now BULK_PRICING. */
|
||||||
// TODO(sarahbot@): Remove this type once all tokens of this type have been scrubbed from the
|
// TODO(sarahbot@): Remove this type once all tokens of this type have been scrubbed from the
|
||||||
// database
|
// database
|
||||||
@Deprecated
|
@Deprecated
|
||||||
PACKAGE,
|
PACKAGE(/* isOneTimeUse= */ false),
|
||||||
/** Invalid after use */
|
/** Invalid after use */
|
||||||
SINGLE_USE,
|
SINGLE_USE(/* isOneTimeUse= */ true),
|
||||||
/** Do not expire after use */
|
/** Do not expire after use */
|
||||||
UNLIMITED_USE,
|
UNLIMITED_USE(/* isOneTimeUse= */ false),
|
||||||
|
/**
|
||||||
|
* Allows bypassing the BSA check during domain creation, otherwise has the same semantics as
|
||||||
|
* {@link #SINGLE_USE}.
|
||||||
|
*
|
||||||
|
* <p>This token applies to a single domain only. If the domain is not blocked by BSA at the
|
||||||
|
* redemption time this token is processed like {@code SINGLE_USE}, as mentioned above.
|
||||||
|
*/
|
||||||
|
REGISTER_BSA(/* isOneTimeUse= */ true);
|
||||||
|
|
||||||
|
private final boolean isOneTimeUse;
|
||||||
|
|
||||||
|
private TokenType(boolean isOneTimeUse) {
|
||||||
|
this.isOneTimeUse = isOneTimeUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if token should be invalidated after use. */
|
||||||
|
public boolean isOneTimeUse() {
|
||||||
|
return this.isOneTimeUse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,12 +381,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||||
|| !getInstance().discountPremiums,
|
|| !getInstance().discountPremiums,
|
||||||
"Bulk tokens cannot discount premium names");
|
"Bulk tokens cannot discount premium names");
|
||||||
checkArgument(
|
checkArgument(
|
||||||
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
getInstance().domainName == null || getInstance().tokenType.isOneTimeUse(),
|
||||||
"Domain name can only be specified for SINGLE_USE tokens");
|
"Domain name can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||||
checkArgument(
|
checkArgument(
|
||||||
getInstance().redemptionHistoryId == null
|
getInstance().redemptionHistoryId == null || getInstance().tokenType.isOneTimeUse(),
|
||||||
|| TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
"Redemption history entry can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||||
"Redemption history entry can only be specified for SINGLE_USE tokens");
|
|
||||||
checkArgument(
|
checkArgument(
|
||||||
getInstance().tokenType != TokenType.BULK_PRICING
|
getInstance().tokenType != TokenType.BULK_PRICING
|
||||||
|| (getInstance().allowedClientIds != null
|
|| (getInstance().allowedClientIds != null
|
||||||
|
@ -378,6 +397,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||||
checkArgument(
|
checkArgument(
|
||||||
getInstance().discountFraction > 0 || getInstance().discountYears == 1,
|
getInstance().discountFraction > 0 || getInstance().discountYears == 1,
|
||||||
"Discount years can only be specified along with a discount fraction");
|
"Discount years can only be specified along with a discount fraction");
|
||||||
|
if (getInstance().getTokenType().equals(REGISTER_BSA)) {
|
||||||
|
checkArgumentNotNull(
|
||||||
|
getInstance().domainName, "REGISTER_BSA tokens must be tied to a domain");
|
||||||
|
}
|
||||||
if (getInstance().registrationBehavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
if (getInstance().registrationBehavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||||
checkArgumentNotNull(
|
checkArgumentNotNull(
|
||||||
getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain");
|
getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain");
|
||||||
|
|
|
@ -18,7 +18,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
import static com.google.common.collect.Iterables.partition;
|
import static com.google.common.collect.Iterables.partition;
|
||||||
import static com.google.common.collect.Streams.stream;
|
import static com.google.common.collect.Streams.stream;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
|
@ -78,7 +77,7 @@ final class DeleteAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||||
ImmutableSet<VKey<AllocationToken>> tokensToDelete =
|
ImmutableSet<VKey<AllocationToken>> tokensToDelete =
|
||||||
tm().loadByKeys(batch).values().stream()
|
tm().loadByKeys(batch).values().stream()
|
||||||
.filter(t -> withDomains || !t.getDomainName().isPresent())
|
.filter(t -> withDomains || !t.getDomainName().isPresent())
|
||||||
.filter(t -> SINGLE_USE.equals(t.getTokenType()))
|
.filter(t -> t.getTokenType().isOneTimeUse())
|
||||||
.filter(t -> !t.isRedeemed())
|
.filter(t -> !t.isRedeemed())
|
||||||
.map(AllocationToken::createVKey)
|
.map(AllocationToken::createVKey)
|
||||||
.collect(toImmutableSet());
|
.collect(toImmutableSet());
|
||||||
|
|
|
@ -19,6 +19,7 @@ import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEF
|
||||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.model.eppoutput.CheckData.DomainCheck.create;
|
import static google.registry.model.eppoutput.CheckData.DomainCheck.create;
|
||||||
|
@ -189,6 +190,40 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||||
create(true, "example3.tld", null));
|
create(true, "example3.tld", null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_bsaBlocked_createAllowedWithToken() throws Exception {
|
||||||
|
persistBsaLabel("example1");
|
||||||
|
setEppInput("domain_check_allocationtoken.xml");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(REGISTER_BSA)
|
||||||
|
.setDomainName("example1.tld")
|
||||||
|
.build());
|
||||||
|
doCheckTest(
|
||||||
|
create(true, "example1.tld", null),
|
||||||
|
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||||
|
create(false, "reserved.tld", "Reserved"),
|
||||||
|
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_bsaBlocked_withIrrelevantTokenType() throws Exception {
|
||||||
|
persistBsaLabel("example1");
|
||||||
|
setEppInput("domain_check_allocationtoken.xml");
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setDomainName("example1.tld")
|
||||||
|
.build());
|
||||||
|
doCheckTest(
|
||||||
|
create(false, "example1.tld", "Blocked by a GlobalBlock service"),
|
||||||
|
create(false, "example2.tld", "Alloc token invalid for domain"),
|
||||||
|
create(false, "reserved.tld", "Reserved"),
|
||||||
|
create(false, "specificuse.tld", "Reserved; alloc. token required"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_clTridNotSpecified() throws Exception {
|
void testSuccess_clTridNotSpecified() throws Exception {
|
||||||
setEppInput("domain_check_no_cltrid.xml");
|
setEppInput("domain_check_no_cltrid.xml");
|
||||||
|
|
|
@ -29,6 +29,7 @@ import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPE
|
||||||
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
|
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
|
||||||
|
@ -255,6 +256,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||||
persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY));
|
persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enrollTldInBsa() {
|
||||||
|
persistResource(
|
||||||
|
Tld.get("tld")
|
||||||
|
.asBuilder()
|
||||||
|
.setBsaEnrollStartTime(Optional.of(clock.nowUtc().minusSeconds(1)))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create host and contact entries for testing.
|
* Create host and contact entries for testing.
|
||||||
*
|
*
|
||||||
|
@ -2593,12 +2602,92 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_blockedByBsa() throws Exception {
|
void testSuccess_blockedByBsa_hasRegisterBsaToken() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
|
allocationToken =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(REGISTER_BSA)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
persistBsaLabel("example");
|
||||||
|
persistContactsAndHosts();
|
||||||
|
setEppInput(
|
||||||
|
"domain_create_allocationtoken.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
runFlow();
|
||||||
|
assertSuccessfulCreate("tld", ImmutableSet.of(), allocationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_blockedByBsa_reservedDomain_viaAllocationTokenExtension() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
|
allocationToken =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(REGISTER_BSA)
|
||||||
|
.setDomainName("resdom.tld")
|
||||||
|
.build());
|
||||||
|
persistBsaLabel("resdom");
|
||||||
|
setEppInput(
|
||||||
|
"domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "resdom.tld", "YEARS", "2"));
|
||||||
|
persistContactsAndHosts();
|
||||||
|
runFlowAssertResponse(
|
||||||
|
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "resdom.tld")));
|
||||||
|
assertSuccessfulCreate("tld", ImmutableSet.of(RESERVED), allocationToken);
|
||||||
|
assertNoLordn();
|
||||||
|
assertAllocationTokenWasRedeemed("abc123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_blockedByBsa_quietPeriod_skipTldStateCheckWithToken() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
|
AllocationToken token =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(REGISTER_BSA)
|
||||||
|
.setRegistrationBehavior(RegistrationBehavior.BYPASS_TLD_STATE)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
persistContactsAndHosts();
|
||||||
|
persistBsaLabel("example");
|
||||||
|
setEppInput(
|
||||||
|
"domain_create_allocationtoken.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
persistResource(
|
persistResource(
|
||||||
Tld.get("tld")
|
Tld.get("tld")
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.setBsaEnrollStartTime(Optional.of(clock.nowUtc().minusSeconds(1)))
|
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, QUIET_PERIOD))
|
||||||
.build());
|
.build());
|
||||||
|
runFlow();
|
||||||
|
assertSuccessfulCreate("tld", ImmutableSet.of(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_blockedByBsa_anchorTenant() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
|
allocationToken =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abcDEF23456")
|
||||||
|
.setTokenType(REGISTER_BSA)
|
||||||
|
.setDomainName("anchor.tld")
|
||||||
|
.build());
|
||||||
|
setEppInput("domain_create_anchor_allocationtoken.xml");
|
||||||
|
persistContactsAndHosts();
|
||||||
|
persistBsaLabel("anchor");
|
||||||
|
runFlowAssertResponse(loadFile("domain_create_anchor_response.xml"));
|
||||||
|
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
|
||||||
|
assertNoLordn();
|
||||||
|
assertAllocationTokenWasRedeemed("abcDEF23456");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_blockedByBsa() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
persistBsaLabel("example");
|
persistBsaLabel("example");
|
||||||
persistContactsAndHosts();
|
persistContactsAndHosts();
|
||||||
EppException thrown = assertThrows(DomainLabelBlockedByBsaException.class, this::runFlow);
|
EppException thrown = assertThrows(DomainLabelBlockedByBsaException.class, this::runFlow);
|
||||||
|
@ -2619,6 +2708,41 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||||
.isEqualTo(loadFile("domain_create_blocked_by_bsa.xml"));
|
.isEqualTo(loadFile("domain_create_blocked_by_bsa.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailure_blockedByBsa_hasWrongToken() throws Exception {
|
||||||
|
enrollTldInBsa();
|
||||||
|
allocationToken =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(SINGLE_USE)
|
||||||
|
.setRegistrationBehavior(RegistrationBehavior.BYPASS_TLD_STATE)
|
||||||
|
.setDomainName("example.tld")
|
||||||
|
.build());
|
||||||
|
persistBsaLabel("example");
|
||||||
|
persistContactsAndHosts();
|
||||||
|
setEppInput(
|
||||||
|
"domain_create_allocationtoken.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||||
|
EppException thrown = assertThrows(DomainLabelBlockedByBsaException.class, this::runFlow);
|
||||||
|
assertAboutEppExceptions()
|
||||||
|
.that(thrown)
|
||||||
|
.marshalsToXml()
|
||||||
|
.and()
|
||||||
|
.hasMessage("Domain label is blocked by the Brand Safety Alliance");
|
||||||
|
byte[] responseXmlBytes =
|
||||||
|
marshal(
|
||||||
|
EppOutput.create(
|
||||||
|
new EppResponse.Builder()
|
||||||
|
.setTrid(Trid.create(null, "server-trid"))
|
||||||
|
.setResult(thrown.getResult())
|
||||||
|
.build()),
|
||||||
|
ValidationMode.STRICT);
|
||||||
|
assertThat(new String(responseXmlBytes, StandardCharsets.UTF_8))
|
||||||
|
.isEqualTo(loadFile("domain_create_blocked_by_bsa.xml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_uppercase() {
|
void testFailure_uppercase() {
|
||||||
doFailingDomainNameTest("Example.tld", BadDomainNameCharacterException.class);
|
doFailingDomainNameTest("Example.tld", BadDomainNameCharacterException.class);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.END
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||||
|
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
|
@ -245,6 +246,18 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||||
.isEqualTo("Bulk tokens may only be valid for CREATE actions");
|
.isEqualTo("Bulk tokens may only be valid for CREATE actions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBuild_registerBsa_missingDomain() {
|
||||||
|
createTld("tld");
|
||||||
|
// REGISTER_BSA requires a domain
|
||||||
|
AllocationToken.Builder token =
|
||||||
|
new AllocationToken.Builder().setToken("abc").setTokenType(REGISTER_BSA);
|
||||||
|
assertThat(assertThrows(IllegalArgumentException.class, () -> token.build()))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo("REGISTER_BSA tokens must be tied to a domain");
|
||||||
|
token.setDomainName("example.tld").build();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFail_bulkTokenNullEppActions() {
|
void testFail_bulkTokenNullEppActions() {
|
||||||
AllocationToken.Builder builder =
|
AllocationToken.Builder builder =
|
||||||
|
@ -317,7 +330,7 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.isEqualTo("Domain name can only be specified for SINGLE_USE tokens");
|
.isEqualTo("Domain name can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -347,7 +360,8 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||||
assertThat(thrown)
|
assertThat(thrown)
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.isEqualTo("Redemption history entry can only be specified for SINGLE_USE tokens");
|
.isEqualTo(
|
||||||
|
"Redemption history entry can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -380,7 +380,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
"Invalid value for -t parameter. Allowed values:[BULK_PRICING, DEFAULT_PROMO, PACKAGE,"
|
"Invalid value for -t parameter. Allowed values:[BULK_PRICING, DEFAULT_PROMO, PACKAGE,"
|
||||||
+ " SINGLE_USE, UNLIMITED_USE]");
|
+ " SINGLE_USE, UNLIMITED_USE, REGISTER_BSA]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue