mirror of
https://github.com/google/nomulus.git
synced 2025-07-09 12:43:24 +02:00
Add a registration_behavior column to AllocationToken (#1695)
This is, as of now, unused but we can use it for b/237683906 and b/237800445 in the future to allow for special behavior dictated by allocation tokens rather than having to reserve specific domains. Note that we enforce a tied domain for ANCHOR_TENANT tokens (because they should be matched to a domain) but not for BYPASS_TLD_STATE tokens.
This commit is contained in:
parent
ffe5a201b3
commit
6af385bcf1
10 changed files with 217 additions and 6 deletions
|
@ -86,6 +86,22 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
.putAll(VALID, ENDED, CANCELLED)
|
||||
.build();
|
||||
|
||||
/** Any special behavior that should be used when registering domains using this token. */
|
||||
public enum RegistrationBehavior {
|
||||
/** No special behavior */
|
||||
DEFAULT,
|
||||
/**
|
||||
* Bypasses the TLD state check, e.g. allowing registration during QUIET_PERIOD.
|
||||
*
|
||||
* <p>NB: while this means that, for instance, one can register non-trademarked domains in the
|
||||
* sunrise period, any trademarked-domain registrations in the sunrise period must still include
|
||||
* the proper signed marks. In other words, this only bypasses the TLD state check.
|
||||
*/
|
||||
BYPASS_TLD_STATE,
|
||||
/** Bypasses most checks and creates the domain as an anchor tenant, with all that implies. */
|
||||
ANCHOR_TENANT
|
||||
}
|
||||
|
||||
/** Single-use tokens are invalid after use. Infinite-use tokens, predictably, are not. */
|
||||
public enum TokenType {
|
||||
SINGLE_USE,
|
||||
|
@ -153,6 +169,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
|
||||
|
||||
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
|
||||
@OnLoad
|
||||
void onLoad() {
|
||||
|
@ -225,6 +245,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public RegistrationBehavior getRegistrationBehavior() {
|
||||
return registrationBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<AllocationToken> createVKey() {
|
||||
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
||||
|
@ -261,6 +285,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
checkArgument(
|
||||
getInstance().discountFraction > 0 || getInstance().discountYears == 1,
|
||||
"Discount years can only be specified along with a discount fraction");
|
||||
if (getInstance().registrationBehavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain");
|
||||
}
|
||||
if (getInstance().domainName != null) {
|
||||
try {
|
||||
DomainFlowUtils.validateDomainName(getInstance().domainName);
|
||||
|
@ -352,5 +380,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
|||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistrationBehavior(RegistrationBehavior registrationBehavior) {
|
||||
getInstance().registrationBehavior = registrationBehavior;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import com.google.common.collect.Streams;
|
|||
import com.google.common.io.Files;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.persistence.VKey;
|
||||
|
@ -152,6 +153,14 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
|||
+ " same as the domain's calculated create price.")
|
||||
private RenewalPriceBehavior renewalPriceBehavior = DEFAULT;
|
||||
|
||||
@Parameter(
|
||||
names = {"--registration_behavior"},
|
||||
description =
|
||||
"Any special registration behavior, including DEFAULT (no special behavior),"
|
||||
+ " BYPASS_TLD_STATE (allow registrations during e.g. QUIET_PERIOD), and"
|
||||
+ " ANCHOR_TENANT (used for anchor tenant registrations")
|
||||
private RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
|
||||
|
||||
@Parameter(
|
||||
names = {"--dry_run"},
|
||||
description = "Do not actually persist the tokens; defaults to false")
|
||||
|
@ -196,6 +205,7 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
|||
new AllocationToken.Builder()
|
||||
.setToken(t)
|
||||
.setRenewalPriceBehavior(renewalPriceBehavior)
|
||||
.setRegistrationBehavior(registrationBehavior)
|
||||
.setTokenType(tokenType == null ? SINGLE_USE : tokenType)
|
||||
.setAllowedRegistrarIds(
|
||||
ImmutableSet.copyOf(nullToEmpty(allowedClientIds)))
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableSet;
|
|||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import java.util.List;
|
||||
|
@ -100,6 +101,15 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
|||
@Nullable
|
||||
private RenewalPriceBehavior renewalPriceBehavior;
|
||||
|
||||
@Parameter(
|
||||
names = {"--registration_behavior"},
|
||||
description =
|
||||
"Any special registration behavior, including DEFAULT (no special behavior),"
|
||||
+ " BYPASS_TLD_STATE (allow registrations during e.g. QUIET_PERIOD), and"
|
||||
+ " ANCHOR_TENANT (used for anchor tenant registrations")
|
||||
@Nullable
|
||||
private RegistrationBehavior registrationBehavior;
|
||||
|
||||
private static final int BATCH_SIZE = 20;
|
||||
private static final Joiner JOINER = Joiner.on(", ");
|
||||
|
||||
|
@ -156,8 +166,8 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
|||
Optional.ofNullable(discountPremiums).ifPresent(builder::setDiscountPremiums);
|
||||
Optional.ofNullable(discountYears).ifPresent(builder::setDiscountYears);
|
||||
Optional.ofNullable(tokenStatusTransitions).ifPresent(builder::setTokenStatusTransitions);
|
||||
Optional.ofNullable(renewalPriceBehavior)
|
||||
.ifPresent(behavior -> builder.setRenewalPriceBehavior(renewalPriceBehavior));
|
||||
Optional.ofNullable(renewalPriceBehavior).ifPresent(builder::setRenewalPriceBehavior);
|
||||
Optional.ofNullable(registrationBehavior).ifPresent(builder::setRegistrationBehavior);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import com.googlecode.objectify.Key;
|
|||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
|
@ -449,6 +450,34 @@ public class AllocationTokenTest extends EntityTestCase {
|
|||
.isEqualTo("Discount years can only be specified along with a discount fraction");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_registrationBehaviors() {
|
||||
createTld("tld");
|
||||
// BYPASS_TLD_STATE doesn't require a domain
|
||||
AllocationToken token =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRegistrationBehavior(RegistrationBehavior.BYPASS_TLD_STATE)
|
||||
.build();
|
||||
// ANCHOR_TENANT does
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
token
|
||||
.asBuilder()
|
||||
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
|
||||
.build()))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("ANCHOR_TENANT tokens must be tied to a domain");
|
||||
token
|
||||
.asBuilder()
|
||||
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
|
||||
.setDomainName("example.tld")
|
||||
.build();
|
||||
}
|
||||
|
||||
private void assertBadInitialTransition(TokenStatus status) {
|
||||
assertBadTransition(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
|
||||
|
@ -260,6 +261,64 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
|||
+ " NONPREMIUM, SPECIFIED]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_defaultRegistrationBehavior() throws Exception {
|
||||
runCommand("--tokens", "foobar,blah");
|
||||
assertThat(
|
||||
loadAllOf(AllocationToken.class).stream()
|
||||
.map(AllocationToken::getRegistrationBehavior)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(
|
||||
AllocationToken.RegistrationBehavior.DEFAULT,
|
||||
AllocationToken.RegistrationBehavior.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_defaultRegistrationBehavior_specified() throws Exception {
|
||||
runCommand("--tokens", "foobar,blah", "--registration_behavior", "DEFAULT");
|
||||
assertThat(
|
||||
loadAllOf(AllocationToken.class).stream()
|
||||
.map(AllocationToken::getRegistrationBehavior)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(
|
||||
AllocationToken.RegistrationBehavior.DEFAULT,
|
||||
AllocationToken.RegistrationBehavior.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_specifiedRegistrationBehavior() throws Exception {
|
||||
runCommand("--tokens", "foobar,blah", "--registration_behavior", "BYPASS_TLD_STATE");
|
||||
assertThat(
|
||||
loadAllOf(AllocationToken.class).stream()
|
||||
.map(AllocationToken::getRegistrationBehavior)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(
|
||||
AllocationToken.RegistrationBehavior.BYPASS_TLD_STATE,
|
||||
AllocationToken.RegistrationBehavior.BYPASS_TLD_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidRegistrationBehaviors() throws Exception {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior")))
|
||||
.hasMessageThat()
|
||||
.contains("Expected a value after parameter --registration_behavior");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior", "bad")))
|
||||
.hasMessageThat()
|
||||
.contains("Invalid value for --registration_behavior");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior", "")))
|
||||
.hasMessageThat()
|
||||
.contains("Invalid value for --registration_behavior");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_specifyManyTokens() throws Exception {
|
||||
command.stringGenerator =
|
||||
|
|
|
@ -24,6 +24,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT
|
|||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
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.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
@ -33,6 +34,7 @@ import com.beust.jcommander.ParameterException;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -190,6 +192,67 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
|||
+ " NONPREMIUM, SPECIFIED]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrationBehavior_same() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
builderWithPromo()
|
||||
.setRegistrationBehavior(AllocationToken.RegistrationBehavior.BYPASS_TLD_STATE)
|
||||
.build());
|
||||
assertThat(token.getRegistrationBehavior())
|
||||
.isEqualTo(AllocationToken.RegistrationBehavior.BYPASS_TLD_STATE);
|
||||
runCommandForced("--tokens", "token", "--registration_behavior", "BYPASS_TLD_STATE");
|
||||
assertThat(loadByEntity(token).getRegistrationBehavior())
|
||||
.isEqualTo(AllocationToken.RegistrationBehavior.BYPASS_TLD_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrationBehavior_different() throws Exception {
|
||||
AllocationToken token = persistResource(builderWithPromo().build());
|
||||
assertThat(token.getRegistrationBehavior())
|
||||
.isEqualTo(AllocationToken.RegistrationBehavior.DEFAULT);
|
||||
runCommandForced("--tokens", "token", "--registration_behavior", "BYPASS_TLD_STATE");
|
||||
assertThat(loadByEntity(token).getRegistrationBehavior())
|
||||
.isEqualTo(RegistrationBehavior.BYPASS_TLD_STATE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_registrationBehavior_enforcesAnchorTenantRestriction() throws Exception {
|
||||
AllocationToken token = persistResource(builderWithPromo().build());
|
||||
assertThat(token.getRegistrationBehavior())
|
||||
.isEqualTo(AllocationToken.RegistrationBehavior.DEFAULT);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--tokens", "token", "--registration_behavior", "ANCHOR_TENANT")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("ANCHOR_TENANT tokens must be tied to a domain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_registrationBehavior_invalid() throws Exception {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior")))
|
||||
.hasMessageThat()
|
||||
.contains("Expected a value after parameter --registration_behavior");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior", "bad")))
|
||||
.hasMessageThat()
|
||||
.contains("Invalid value for --registration_behavior");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--registration_behavior", "")))
|
||||
.hasMessageThat()
|
||||
.contains("Invalid value for --registration_behavior");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateStatusTransitions() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
|
|
|
@ -338,6 +338,7 @@ class google.registry.model.domain.token.AllocationToken {
|
|||
google.registry.model.UpdateAutoTimestamp updateTimestamp;
|
||||
google.registry.model.billing.BillingEvent$RenewalPriceBehavior renewalPriceBehavior;
|
||||
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> tokenStatusTransitions;
|
||||
google.registry.model.domain.token.AllocationToken$RegistrationBehavior registrationBehavior;
|
||||
google.registry.model.domain.token.AllocationToken$TokenType tokenType;
|
||||
google.registry.persistence.DomainHistoryVKey redemptionHistoryEntry;
|
||||
int discountYears;
|
||||
|
@ -345,6 +346,11 @@ class google.registry.model.domain.token.AllocationToken {
|
|||
java.util.Set<java.lang.String> allowedClientIds;
|
||||
java.util.Set<java.lang.String> allowedTlds;
|
||||
}
|
||||
enum google.registry.model.domain.token.AllocationToken$RegistrationBehavior {
|
||||
ANCHOR_TENANT;
|
||||
BYPASS_TLD_STATE;
|
||||
DEFAULT;
|
||||
}
|
||||
enum google.registry.model.domain.token.AllocationToken$TokenStatus {
|
||||
CANCELLED;
|
||||
ENDED;
|
||||
|
|
|
@ -261,7 +261,7 @@ td.section {
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2022-07-15 16:41:11.765867</td>
|
||||
<td class="property_value">2022-07-15 18:56:30.776055</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
|
@ -284,7 +284,7 @@ td.section {
|
|||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4055.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2022-07-15 16:41:11.765867
|
||||
2022-07-15 18:56:30.776055
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3968,-4 3968,-44 4233,-44 4233,-4 3968,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
|
|
@ -261,7 +261,7 @@ td.section {
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2022-07-15 16:41:09.696997</td>
|
||||
<td class="property_value">2022-07-15 18:56:28.677292</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
|
@ -284,7 +284,7 @@ td.section {
|
|||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4755.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2022-07-15 16:41:09.696997
|
||||
2022-07-15 18:56:28.677292
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="4668.02,-4 4668.02,-44 4933.02,-44 4933.02,-4 4668.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
domain_name text,
|
||||
redemption_domain_history_id int8,
|
||||
redemption_domain_repo_id text,
|
||||
registration_behavior text not null,
|
||||
renewal_price_behavior text not null,
|
||||
token_status_transitions hstore,
|
||||
token_type text,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue