mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 00:17:20 +02:00
Validate on-load that an AllocationToken can be used
Check the timing (that is, whether or not we're in a promotion), the allowed registrar client IDs, and the allowed TLDs. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=246824080
This commit is contained in:
parent
1480181fe1
commit
df7e9a1225
9 changed files with 344 additions and 55 deletions
|
@ -344,6 +344,7 @@ An EPP flow that creates a new domain resource.
|
|||
* 2303
|
||||
* Resource linked to this domain does not exist.
|
||||
* 2304
|
||||
* The allocation token is not currently valid.
|
||||
* The claims period for this TLD has ended.
|
||||
* Requested domain is reserved.
|
||||
* Linked resource in pending delete prohibits operation.
|
||||
|
@ -355,6 +356,8 @@ An EPP flow that creates a new domain resource.
|
|||
* Registrant is not whitelisted for this TLD.
|
||||
* Requested domain does not require a claims notice.
|
||||
* 2305
|
||||
* The allocation token is not valid for this registrar.
|
||||
* The allocation token is not valid for this TLD.
|
||||
* The allocation token was already redeemed.
|
||||
* 2306
|
||||
* Anchor tenant domain create is for the wrong number of years.
|
||||
|
|
|
@ -114,6 +114,9 @@ import org.joda.time.Duration;
|
|||
/**
|
||||
* An EPP flow that creates a new domain resource.
|
||||
*
|
||||
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
|
||||
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
|
||||
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
|
||||
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
|
||||
* @error {@link google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
|
||||
* @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException}
|
||||
|
@ -445,7 +448,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
||||
return Optional.ofNullable(
|
||||
extension.isPresent()
|
||||
? allocationTokenFlowUtils.loadAndVerifyToken(
|
||||
? allocationTokenFlowUtils.loadTokenAndValidateDomainCreate(
|
||||
command, extension.get().getAllocationToken(), registry, clientId, now)
|
||||
: null);
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ import org.joda.time.DateTime;
|
|||
*/
|
||||
public class AllocationTokenCustomLogic {
|
||||
|
||||
/** Performs additional custom logic for verifying a token. */
|
||||
public AllocationToken verifyToken(
|
||||
/** Performs additional custom logic for validating a token. */
|
||||
public AllocationToken validateToken(
|
||||
DomainCommand.Create command,
|
||||
AllocationToken token,
|
||||
Registry registry,
|
||||
|
|
|
@ -25,8 +25,10 @@ import com.googlecode.objectify.Key;
|
|||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
|
@ -46,16 +48,19 @@ public class AllocationTokenFlowUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Verifies that a given allocation token string is valid.
|
||||
* Loads an allocation token given a string and verifies that the token is valid for the domain
|
||||
* create request.
|
||||
*
|
||||
* @return the loaded {@link AllocationToken} for that string.
|
||||
* @throws InvalidAllocationTokenException if the token doesn't exist.
|
||||
* @throws EppException if the token doesn't exist, is already redeemed, or is otherwise invalid
|
||||
* for this request.
|
||||
*/
|
||||
public AllocationToken loadAndVerifyToken(
|
||||
public AllocationToken loadTokenAndValidateDomainCreate(
|
||||
DomainCommand.Create command, String token, Registry registry, String clientId, DateTime now)
|
||||
throws EppException {
|
||||
AllocationToken tokenEntity = loadToken(token);
|
||||
return tokenCustomLogic.verifyToken(command, tokenEntity, registry, clientId, now);
|
||||
validateToken(tokenEntity, clientId, registry.getTldStr(), now);
|
||||
return tokenCustomLogic.validateToken(command, tokenEntity, registry, clientId, now);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,24 +72,38 @@ public class AllocationTokenFlowUtils {
|
|||
*/
|
||||
public AllocationTokenDomainCheckResults checkDomainsWithToken(
|
||||
List<InternetDomainName> domainNames, String token, String clientId, DateTime now) {
|
||||
// If the token is completely invalid, return the error message for all domain names
|
||||
AllocationToken tokenEntity;
|
||||
try {
|
||||
AllocationToken tokenEntity = loadToken(token);
|
||||
// Only call custom logic if there wasn't a global allocation token error that applies to all
|
||||
// check results. The custom logic can only add errors, not override existing errors.
|
||||
return AllocationTokenDomainCheckResults.create(
|
||||
Optional.of(tokenEntity),
|
||||
tokenCustomLogic.checkDomainsWithToken(
|
||||
ImmutableList.copyOf(domainNames), tokenEntity, clientId, now));
|
||||
tokenEntity = loadToken(token);
|
||||
} catch (EppException e) {
|
||||
return AllocationTokenDomainCheckResults.create(
|
||||
Optional.empty(),
|
||||
ImmutableMap.copyOf(Maps.toMap(domainNames, ignored -> e.getMessage())));
|
||||
}
|
||||
|
||||
// If the token is only invalid for some domain names (e.g. an invalid TLD), include those error
|
||||
// results for only those domain names
|
||||
ImmutableList.Builder<InternetDomainName> validDomainNames = new ImmutableList.Builder<>();
|
||||
ImmutableMap.Builder<InternetDomainName, String> resultsBuilder = new ImmutableMap.Builder<>();
|
||||
for (InternetDomainName domainName : domainNames) {
|
||||
try {
|
||||
validateToken(tokenEntity, clientId, domainName.parent().toString(), now);
|
||||
validDomainNames.add(domainName);
|
||||
} catch (EppException e) {
|
||||
resultsBuilder.put(domainName, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy.
|
||||
*/
|
||||
// For all valid domain names, run the custom logic and include the results
|
||||
resultsBuilder.putAll(
|
||||
tokenCustomLogic.checkDomainsWithToken(
|
||||
validDomainNames.build(), tokenEntity, clientId, now));
|
||||
return AllocationTokenDomainCheckResults.create(
|
||||
Optional.of(tokenEntity), resultsBuilder.build());
|
||||
}
|
||||
|
||||
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
|
||||
public AllocationToken redeemToken(
|
||||
AllocationToken token, Key<HistoryEntry> redemptionHistoryEntry) {
|
||||
checkArgument(
|
||||
|
@ -93,6 +112,30 @@ public class AllocationTokenFlowUtils {
|
|||
return token.asBuilder().setRedemptionHistoryEntry(redemptionHistoryEntry).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a given token. The token could be invalid if it has allowed client IDs or TLDs that
|
||||
* do not include this client ID / TLD, or if the token has a promotion that is not currently
|
||||
* running.
|
||||
*
|
||||
* @throws EppException if the token is invalid in any way
|
||||
*/
|
||||
private void validateToken(AllocationToken token, String clientId, String tld, DateTime now)
|
||||
throws EppException {
|
||||
if (!token.getAllowedClientIds().isEmpty() && !token.getAllowedClientIds().contains(clientId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
}
|
||||
if (!token.getAllowedTlds().isEmpty() && !token.getAllowedTlds().contains(tld)) {
|
||||
throw new AllocationTokenNotValidForTldException();
|
||||
}
|
||||
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
|
||||
// check the status transitions map if it's non-trivial.
|
||||
if (token.getTokenStatusTransitions().size() > 1
|
||||
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
|
||||
throw new AllocationTokenNotInPromotionException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads a given token and validates that it is not redeemed */
|
||||
private AllocationToken loadToken(String token) throws EppException {
|
||||
AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now();
|
||||
if (tokenEntity == null) {
|
||||
|
@ -104,6 +147,31 @@ public class AllocationTokenFlowUtils {
|
|||
return tokenEntity;
|
||||
}
|
||||
|
||||
// Note: exception messages should be <= 32 characters long for domain check results
|
||||
|
||||
/** The allocation token is not currently valid. */
|
||||
public static class AllocationTokenNotInPromotionException
|
||||
extends StatusProhibitsOperationException {
|
||||
public AllocationTokenNotInPromotionException() {
|
||||
super("Alloc token not in promo period");
|
||||
}
|
||||
}
|
||||
/** The allocation token is not valid for this TLD. */
|
||||
public static class AllocationTokenNotValidForTldException
|
||||
extends AssociationProhibitsOperationException {
|
||||
public AllocationTokenNotValidForTldException() {
|
||||
super("Alloc token invalid for TLD");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this registrar. */
|
||||
public static class AllocationTokenNotValidForRegistrarException
|
||||
extends AssociationProhibitsOperationException {
|
||||
public AllocationTokenNotValidForRegistrarException() {
|
||||
super("Alloc token invalid for client");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token was already redeemed. */
|
||||
public static class AlreadyRedeemedAllocationTokenException
|
||||
extends AssociationProhibitsOperationException {
|
||||
|
|
|
@ -186,6 +186,7 @@ public class DomainCheckFlowTest
|
|||
|
||||
@Test
|
||||
public void testSuccess_allocationTokenPromotion() throws Exception {
|
||||
createTld("example");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
|
@ -194,14 +195,82 @@ public class DomainCheckFlowTest
|
|||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusMillis(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promotionNotActive() throws Exception {
|
||||
createTld("example");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token not in promo period"),
|
||||
create(false, "example2.example", "Alloc token not in promo period"),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promoTokenNotValidForTld() throws Exception {
|
||||
createTld("example");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setAllowedTlds(ImmutableSet.of("example"))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for TLD"),
|
||||
create(true, "example2.example", null),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promoTokenNotValidForRegistrar() throws Exception {
|
||||
createTld("example");
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setAllowedClientIds(ImmutableSet.of("someOtherClient"))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_check_allocationtoken_fee.xml");
|
||||
doCheckTest(
|
||||
create(false, "example1.tld", "Alloc token invalid for client"),
|
||||
create(false, "example2.example", "Alloc token invalid for client"),
|
||||
create(false, "reserved.tld", "Reserved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_oneReservedInSunrise() throws Exception {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
|
|
|
@ -128,6 +128,9 @@ import google.registry.flows.domain.DomainFlowUtils.TrailingDashException;
|
|||
import google.registry.flows.domain.DomainFlowUtils.UnexpectedClaimsNoticeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UnsupportedMarkTypeException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
|
@ -472,7 +475,16 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
|
|||
persistContactsAndHosts();
|
||||
allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setTokenType(UNLIMITED_USE).setToken("abc123").build());
|
||||
new AllocationToken.Builder()
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setToken("abc123")
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlow();
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(), allocationToken);
|
||||
|
@ -1156,6 +1168,71 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
|
|||
.isEqualTo("A nonzero discount code cannot be applied to premium domains");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_promotionNotActive() {
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(60), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenNotInPromotionException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promoTokenNotValidForTld() {
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setAllowedTlds(ImmutableSet.of("example"))
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenNotValidForTldException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_promoTokenNotValidForRegistrar() {
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.UNLIMITED_USE)
|
||||
.setAllowedClientIds(ImmutableSet.of("someClientId"))
|
||||
.setDiscountFraction(0.5)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
|
||||
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
|
||||
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
|
||||
.build())
|
||||
.build());
|
||||
setEppInput("domain_create_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(AllocationTokenNotValidForRegistrarException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_superuserReserved() throws Exception {
|
||||
setEppInput("domain_create_reserved.xml");
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<domain:check
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example1.tld</domain:name>
|
||||
<domain:name>example2.tld</domain:name>
|
||||
<domain:name>example2.example</domain:name>
|
||||
<domain:name>reserved.tld</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
<fee:domain>
|
||||
<fee:name>example2.tld</fee:name>
|
||||
<fee:name>example2.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<domain:name avail="true">example1.tld</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="true">example2.tld</domain:name>
|
||||
<domain:name avail="true">example2.example</domain:name>
|
||||
</domain:cd>
|
||||
<domain:cd>
|
||||
<domain:name avail="false">reserved.tld</domain:name>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<extension>
|
||||
<fee:chkData>
|
||||
<fee:cd>
|
||||
<fee:name>example2.tld</fee:name>
|
||||
<fee:name>example2.example</fee:name>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
|
|
|
@ -15,23 +15,35 @@
|
|||
package google.registry.flows.domain.token;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.CANCELLED;
|
||||
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.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.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
|
@ -48,6 +60,9 @@ import org.junit.runners.JUnit4;
|
|||
@RunWith(JUnit4.class)
|
||||
public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
||||
|
||||
private final AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
|
||||
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||
|
||||
@Before
|
||||
|
@ -56,14 +71,12 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_verifyToken_successfullyVerifiesValidToken() throws Exception {
|
||||
public void test_validateToken_successfullyVerifiesValidToken() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils.loadAndVerifyToken(
|
||||
flowUtils.loadTokenAndValidateDomainCreate(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
|
@ -73,33 +86,21 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_verifyToken_failsOnNonexistentToken() {
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
EppException thrown =
|
||||
assertThrows(
|
||||
InvalidAllocationTokenException.class,
|
||||
() ->
|
||||
flowUtils.loadAndVerifyToken(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC)));
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
public void test_validateToken_failsOnNonexistentToken() {
|
||||
assertValidateThrowsEppException(InvalidAllocationTokenException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_verifyToken_callsCustomLogic() {
|
||||
public void test_validateToken_callsCustomLogic() {
|
||||
AllocationTokenFlowUtils failingFlowUtils =
|
||||
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
||||
Exception thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
flowUtils.loadAndVerifyToken(
|
||||
failingFlowUtils.loadTokenAndValidateDomainCreate(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
|
@ -108,12 +109,55 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_validateToken_invalidForClientId() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedClientIds(ImmutableSet.of("NewRegistrar"))
|
||||
.build());
|
||||
assertValidateThrowsEppException(AllocationTokenNotValidForRegistrarException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_validateToken_invalidForTld() {
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setAllowedTlds(ImmutableSet.of("nottld"))
|
||||
.build());
|
||||
assertValidateThrowsEppException(AllocationTokenNotValidForTldException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_validateToken_beforePromoStart() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).plusDays(1)).build());
|
||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_validateToken_afterPromoEnd() {
|
||||
persistResource(createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusMonths(2)).build());
|
||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_validateToken_promoCancelled() {
|
||||
// the promo would be valid but it was cancelled 12 hours ago
|
||||
persistResource(
|
||||
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, NOT_STARTED)
|
||||
.put(DateTime.now(UTC).minusMonths(1), VALID)
|
||||
.put(DateTime.now(UTC).minusHours(12), CANCELLED)
|
||||
.build())
|
||||
.build());
|
||||
assertValidateThrowsEppException(AllocationTokenNotInPromotionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_checkDomainsWithToken_successfullyVerifiesValidToken() {
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
|
@ -137,8 +181,6 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
.setTokenType(SINGLE_USE)
|
||||
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 101L))
|
||||
.build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils
|
||||
.checkDomainsWithToken(
|
||||
|
@ -161,13 +203,13 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
public void test_checkDomainsWithToken_callsCustomLogic() {
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
AllocationTokenFlowUtils failingFlowUtils =
|
||||
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
|
||||
Exception thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
flowUtils.checkDomainsWithToken(
|
||||
failingFlowUtils.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
|
||||
"tokeN",
|
||||
|
@ -180,10 +222,10 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
public void test_checkDomainsWithToken_resultsFromCustomLogicAreIntegrated() {
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
|
||||
AllocationTokenFlowUtils flowUtils =
|
||||
AllocationTokenFlowUtils customResultFlowUtils =
|
||||
new AllocationTokenFlowUtils(new CustomResultAllocationTokenCustomLogic());
|
||||
assertThat(
|
||||
flowUtils
|
||||
customResultFlowUtils
|
||||
.checkDomainsWithToken(
|
||||
ImmutableList.of(
|
||||
InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")),
|
||||
|
@ -200,17 +242,44 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase {
|
|||
.inOrder();
|
||||
}
|
||||
|
||||
private void assertValidateThrowsEppException(Class<? extends EppException> clazz) {
|
||||
assertAboutEppExceptions()
|
||||
.that(
|
||||
assertThrows(
|
||||
clazz,
|
||||
() ->
|
||||
flowUtils.loadTokenAndValidateDomainCreate(
|
||||
createCommand("blah.tld"),
|
||||
"tokeN",
|
||||
Registry.get("tld"),
|
||||
"TheRegistrar",
|
||||
DateTime.now(UTC))))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
private static DomainCommand.Create createCommand(String domainName) {
|
||||
DomainCommand.Create command = mock(DomainCommand.Create.class);
|
||||
when(command.getFullyQualifiedDomainName()).thenReturn(domainName);
|
||||
return command;
|
||||
}
|
||||
|
||||
private static AllocationToken.Builder createOneMonthPromoTokenBuilder(DateTime promoStart) {
|
||||
return new AllocationToken.Builder()
|
||||
.setToken("tokeN")
|
||||
.setTokenType(UNLIMITED_USE)
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
.put(START_OF_TIME, NOT_STARTED)
|
||||
.put(promoStart, VALID)
|
||||
.put(promoStart.plusMonths(1), ENDED)
|
||||
.build());
|
||||
}
|
||||
|
||||
/** An {@link AllocationTokenCustomLogic} class that throws exceptions on every method. */
|
||||
private static class FailingAllocationTokenCustomLogic extends AllocationTokenCustomLogic {
|
||||
|
||||
@Override
|
||||
public AllocationToken verifyToken(
|
||||
public AllocationToken validateToken(
|
||||
DomainCommand.Create command,
|
||||
AllocationToken token,
|
||||
Registry registry,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue