1
0
mirror of https://github.com/google/nomulus synced 2026-01-05 04:56:03 +00:00

Allow anchor tenant creation via allocation token behavior (#1735)

* Allow anchor tenant creation via allocation token behavior

This also enforces that non-superusers cannot create registrations on
trademarked names prior to the sunrise period, even if they have an
allocation token with ANCHOR_TENANT behavior.
This commit is contained in:
gbrodman
2022-08-15 12:42:16 -04:00
committed by GitHub
parent bbb6174c9f
commit 87e5d19fe5
5 changed files with 355 additions and 9 deletions

View File

@@ -153,6 +153,7 @@ import org.joda.time.Duration;
* @error {@link DomainCreateFlow.AnchorTenantCreatePeriodException}
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
@@ -481,7 +482,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
boolean isValidReservedCreate,
boolean hasSignedMarks)
throws NoGeneralRegistrationsInCurrentPhaseException,
MustHaveSignedMarksInCurrentPhaseException {
MustHaveSignedMarksInCurrentPhaseException,
NoTrademarkedRegistrationsBeforeSunriseException {
// We allow general registration during GA.
TldState currentState = registry.getTldState(now);
if (currentState.equals(GENERAL_AVAILABILITY)) {
@@ -496,16 +498,23 @@ public final class DomainCreateFlow implements TransactionalFlow {
// Bypass most TLD state checks if that behavior is specified by the token
if (behavior.equals(RegistrationBehavior.BYPASS_TLD_STATE)
|| behavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
// If bypassing TLD state checks, a post-sunrise state is always fine
if (!currentState.equals(START_DATE_SUNRISE)
&& registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
return;
}
// Non-trademarked names with the state check bypassed are always available
if (!claimsList.getClaimKey(domainLabel).isPresent()) {
return;
}
if (!currentState.equals(START_DATE_SUNRISE)) {
// Trademarked domains cannot be registered until after the sunrise period has ended, unless
// a valid signed mark is provided. Signed marks can only be provided during sunrise.
// Thus, when bypassing TLD state checks, a post-sunrise state is always fine.
if (registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
return;
} else {
// If sunrise hasn't happened yet, trademarked domains are unavailable
throw new NoTrademarkedRegistrationsBeforeSunriseException(domainLabel);
}
}
}
// Otherwise, signed marks are necessary and sufficient in the sunrise period
if (currentState.equals(START_DATE_SUNRISE)) {
if (!hasSignedMarks) {
@@ -724,6 +733,17 @@ public final class DomainCreateFlow implements TransactionalFlow {
}
}
/** Trademarked domains cannot be registered before the sunrise period. */
static class NoTrademarkedRegistrationsBeforeSunriseException
extends ParameterValuePolicyErrorException {
public NoTrademarkedRegistrationsBeforeSunriseException(String domainLabel) {
super(
String.format(
"The trademarked label %s cannot be registered before the sunrise period.",
domainLabel));
}
}
/** Anchor tenant domain create is for the wrong number of years. */
static class AnchorTenantCreatePeriodException extends ParameterValuePolicyErrorException {
public AnchorTenantCreatePeriodException(int invalidYears) {

View File

@@ -112,6 +112,7 @@ import google.registry.model.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.host.Host;
@@ -270,7 +271,13 @@ public class DomainFlowUtils {
&& token.get().getDomainName().get().equals(domainName.toString())) {
return true;
}
// Otherwise check whether the metadata extension is being used by a superuser to specify that
// Otherwise, check to see if we're using the specialized anchor tenant registration behavior on
// the allocation token
if (token.isPresent()
&& token.get().getRegistrationBehavior().equals(RegistrationBehavior.ANCHOR_TENANT)) {
return true;
}
// Otherwise, check whether the metadata extension is being used by a superuser to specify that
// it's an anchor tenant creation.
return metadataExtension.isPresent() && metadataExtension.get().getIsAnchorTenant();
}

View File

@@ -82,6 +82,7 @@ import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.domain.DomainCreateFlow.AnchorTenantCreatePeriodException;
import google.registry.flows.domain.DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException;
import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException;
import google.registry.flows.domain.DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException;
import google.registry.flows.domain.DomainCreateFlow.RenewalPriceInfo;
import google.registry.flows.domain.DomainCreateFlow.SignedMarksOnlyDuringSunriseException;
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkExpiredException;
@@ -2734,7 +2735,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_quietPeriod_skipTldCheckWithToken() throws Exception {
void testSuccess_quietPeriod_skipTldStateCheckWithToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
@@ -2813,7 +2814,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build());
persistContactsAndHosts();
setEppInput("domain_create_allocationtoken_claims.xml");
assertThrows(NoGeneralRegistrationsInCurrentPhaseException.class, this::runFlow);
assertThrows(NoTrademarkedRegistrationsBeforeSunriseException.class, this::runFlow);
}
@Test
@@ -2844,4 +2845,285 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(), allocationToken);
}
// ________________________________
// Anchor tenant in quiet period before sunrise:
// Only non-trademarked domains are allowed.
// ________________________________
@Test
void testSuccess_anchorTenant_quietPeriodBeforeSunrise_nonTrademarked_viaToken()
throws Exception {
createTld("tld", QUIET_PERIOD);
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
}
@Test
void testFailure_anchorTenant_quietPeriodBeforeSunrise_trademarked_withoutClaims_viaToken() {
createTld("tld", QUIET_PERIOD);
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("test-validate.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "test-validate.tld", "YEARS", "2"));
persistContactsAndHosts();
assertThrows(NoTrademarkedRegistrationsBeforeSunriseException.class, this::runFlow);
}
@Test
void testFailure_anchorTenant_quietPeriodBeforeSunrise_trademarked_withClaims_viaToken() {
createTld("tld", QUIET_PERIOD);
persistResource(
allocationToken
.asBuilder()
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
assertThrows(NoTrademarkedRegistrationsBeforeSunriseException.class, this::runFlow);
}
// ________________________________
// Anchor tenant in sunrise:
// Non-trademarked domains and trademarked domains with signed marks are allowed
// ________________________________
@Test
void testSuccess_anchorTenant_inSunrise_nonTrademarked_viaToken() throws Exception {
createTld("tld", START_DATE_SUNRISE);
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
}
@Test
void testSuccess_anchorTenant_inSunrise_trademarked_withSignedMark_viaToken() throws Exception {
createTld("tld", START_DATE_SUNRISE);
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
setEppInput(
"domain_create_registration_encoded_signed_mark_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "test-validate.tld"));
persistResource(
allocationToken
.asBuilder()
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.setDomainName("test-validate.tld")
.build());
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT), allocationToken);
}
@Test
void testFailure_anchorTenant_inSunrise_trademarked_withoutSignedMark_viaToken() {
createTld("tld", START_DATE_SUNRISE);
persistResource(
new AllocationToken.Builder()
.setTokenType(SINGLE_USE)
.setToken("abc123")
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
persistContactsAndHosts();
assertThrows(MustHaveSignedMarksInCurrentPhaseException.class, this::runFlow);
}
@Test
void testFailure_anchorTenant_inSunrise_trademarked_withoutSignedMark_withClaims_viaToken() {
createTld("tld", START_DATE_SUNRISE);
persistResource(
allocationToken
.asBuilder()
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
assertThrows(MustHaveSignedMarksInCurrentPhaseException.class, this::runFlow);
}
// ________________________________
// Anchor tenant in a post-sunrise quiet period:
// Non-trademarked domains and trademarked domains with claims are allowed.
// ________________________________
@Test
void testSuccess_anchorTenant_quietPeriodAfterSunrise_nonTrademarked_viaToken() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setTldStateTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
QUIET_PERIOD,
clock.nowUtc().minusYears(1),
START_DATE_SUNRISE,
clock.nowUtc().minusMonths(1),
QUIET_PERIOD))
.build());
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.setDomainName("example.tld")
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
}
@Test
void testSuccess_anchorTenant_quietPeriodAfterSunrise_trademarked_withClaims_viaToken()
throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setTldStateTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
QUIET_PERIOD,
clock.nowUtc().minusYears(1),
START_DATE_SUNRISE,
clock.nowUtc().minusMonths(1),
QUIET_PERIOD))
.build());
persistResource(
allocationToken
.asBuilder()
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
}
@Test
void testFailure_anchorTenant_quietPeriodAfterSunrise_trademarked_withoutClaims_viaToken() {
persistResource(
Registry.get("tld")
.asBuilder()
.setTldStateTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
QUIET_PERIOD,
clock.nowUtc().minusYears(1),
START_DATE_SUNRISE,
clock.nowUtc().minusMonths(1),
QUIET_PERIOD))
.build());
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example-one.tld", "YEARS", "2"));
assertThrows(MissingClaimsNoticeException.class, this::runFlow);
}
// ________________________________
// Anchor tenant in GA:
// Non-trademarked domains and trademarked domains with claims are allowed.
// ________________________________
@Test
void testSuccess_anchorTenant_ga_nonTrademarked_viaToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.setDomainName("example.tld")
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), token);
}
@Test
void testSuccess_anchorTenant_ga_trademarked_withClaims_viaToken() throws Exception {
persistResource(
allocationToken
.asBuilder()
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
setEppInput("domain_create_allocationtoken_claims.xml");
persistContactsAndHosts();
runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
}
@Test
void testFailure_anchorTenant_ga_trademarked_withoutClaims_viaToken() {
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example-one.tld")
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example-one.tld", "YEARS", "2"));
assertThrows(MissingClaimsNoticeException.class, this::runFlow);
}
@Test
void testFailure_anchorTenant_mismatchedName_viaToken() throws Exception {
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRegistrationBehavior(RegistrationBehavior.ANCHOR_TENANT)
.setDomainName("example.tld")
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example-one.tld", "YEARS", "2"));
assertThrows(AllocationTokenNotValidForDomainException.class, this::runFlow);
}
}

View File

@@ -374,6 +374,7 @@ An EPP flow that creates a new domain resource.
* The allocation token was already redeemed.
* 2306
* Anchor tenant domain create is for the wrong number of years.
* Trademarked domains cannot be registered before the sunrise period.
* The provided mark is not yet valid.
* The provided mark has expired.
* Domain names can only contain a-z, 0-9, '.' and '-'.