Return more informative errors when signed mark is invalid at this time

A "mark" tells us that the holder owns the trademark for a given domain name. It is signed for authentication.

If the signature's certificate is either "not yet valid" or "expired", we return explicit errors to that effect.

But in addition to the signature's certificate, the mark itself might not be valid yet or already expired. Right now if that happens - we return an error saying "the mark doesn't match the domain name".

That is wrong - as the mark can match the domain name, just be expired. Returning "the mark doesn't match the domain name" in that case is misleading.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=190069976
This commit is contained in:
guyben 2018-03-22 08:39:47 -07:00 committed by jianglai
parent 27dedf316b
commit b5ae37c5cc
6 changed files with 61 additions and 7 deletions

View file

@ -330,6 +330,8 @@ An EPP flow that creates a new application for a domain resource.
phase. phase.
* Domain labels cannot begin with a dash. * Domain labels cannot begin with a dash.
* Missing type attribute for contact. * Missing type attribute for contact.
* The provided mark is not yet valid.
* The provided mark has expired.
* Signed marks must be encoded. * Signed marks must be encoded.
* Certificate used in signed mark signature has expired. * Certificate used in signed mark signature has expired.
* Certificate parsing error, or possibly a bad provider or algorithm. * Certificate parsing error, or possibly a bad provider or algorithm.
@ -580,6 +582,8 @@ An EPP flow that creates a new domain resource.
* 2305 * 2305
* The allocation token was already redeemed. * The allocation token was already redeemed.
* 2306 * 2306
* The provided mark is not yet valid.
* The provided mark has expired.
* Domain names can only contain a-z, 0-9, '.' and '-'. * Domain names can only contain a-z, 0-9, '.' and '-'.
* Periods for domain registrations must be specified in years. * Periods for domain registrations must be specified in years.
* The requested fees cannot be provided in the requested currency. * The requested fees cannot be provided in the requested currency.

View file

@ -144,6 +144,8 @@ import org.joda.time.DateTime;
* @error {@link NameserversNotSpecifiedForNameserverRestrictedDomainException} * @error {@link NameserversNotSpecifiedForNameserverRestrictedDomainException}
* @error {@link NameserversNotSpecifiedForTldWithNameserverWhitelistException} * @error {@link NameserversNotSpecifiedForTldWithNameserverWhitelistException}
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException} * @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
* @error {@link DomainFlowTmchUtils.FoundMarkExpiredException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
* @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException}
@ -167,7 +169,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException} * @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
*/ */
@ReportingSpec(ActivityReportField.DOMAIN_CREATE) // Applications are technically domains in EPP. @ReportingSpec(ActivityReportField.DOMAIN_CREATE) // Applications are technically domains in EPP.
public final class DomainApplicationCreateFlow implements TransactionalFlow { public final class DomainApplicationCreateFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;

View file

@ -124,6 +124,8 @@ import org.joda.time.Duration;
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException} * @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException} * @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
* @error {@link DomainFlowTmchUtils.FoundMarkExpiredException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException} * @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
* @error {@link DomainFlowUtils.BadDomainNameCharacterException} * @error {@link DomainFlowUtils.BadDomainNameCharacterException}

View file

@ -16,7 +16,6 @@ package google.registry.flows.domain;
import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.concat;
import static google.registry.flows.EppXmlTransformer.unmarshal; import static google.registry.flows.EppXmlTransformer.unmarshal;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -111,11 +110,18 @@ public final class DomainFlowTmchUtils {
throw new SignedMarkParsingErrorException(); throw new SignedMarkParsingErrorException();
} }
if (!(isAtOrAfter(now, signedMark.getCreationTime()) if (now.isBefore(signedMark.getCreationTime())) {
&& now.isBefore(signedMark.getExpirationTime()) throw new FoundMarkNotYetValidException();
&& containsMatchingLabel(signedMark.getMark(), domainLabel))) { }
if (now.isAfter(signedMark.getExpirationTime())) {
throw new FoundMarkExpiredException();
}
if (!containsMatchingLabel(signedMark.getMark(), domainLabel)) {
throw new NoMarksFoundMatchingDomainException(); throw new NoMarksFoundMatchingDomainException();
} }
return signedMark; return signedMark;
} }
@ -150,6 +156,20 @@ public final class DomainFlowTmchUtils {
} }
} }
/** The provided mark is not yet valid. */
static class FoundMarkNotYetValidException extends ParameterValuePolicyErrorException {
public FoundMarkNotYetValidException() {
super("The provided mark is not yet valid");
}
}
/** The provided mark has expired. */
static class FoundMarkExpiredException extends ParameterValuePolicyErrorException {
public FoundMarkExpiredException() {
super("The provided mark has expired");
}
}
/** Certificate used in signed mark signature was revoked by ICANN. */ /** Certificate used in signed mark signature was revoked by ICANN. */
static class SignedMarkCertificateRevokedException extends ParameterValuePolicyErrorException { static class SignedMarkCertificateRevokedException extends ParameterValuePolicyErrorException {
public SignedMarkCertificateRevokedException() { public SignedMarkCertificateRevokedException() {

View file

@ -51,6 +51,8 @@ import google.registry.flows.domain.DomainApplicationCreateFlow.NoticeCannotBeUs
import google.registry.flows.domain.DomainApplicationCreateFlow.SunriseApplicationDisallowedDuringLandrushException; import google.registry.flows.domain.DomainApplicationCreateFlow.SunriseApplicationDisallowedDuringLandrushException;
import google.registry.flows.domain.DomainApplicationCreateFlow.UncontestedSunriseApplicationBlockedInLandrushException; import google.registry.flows.domain.DomainApplicationCreateFlow.UncontestedSunriseApplicationBlockedInLandrushException;
import google.registry.flows.domain.DomainFlowTmchUtils.Base64RequiredForEncodedSignedMarksException; import google.registry.flows.domain.DomainFlowTmchUtils.Base64RequiredForEncodedSignedMarksException;
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkExpiredException;
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkNotYetValidException;
import google.registry.flows.domain.DomainFlowTmchUtils.NoMarksFoundMatchingDomainException; import google.registry.flows.domain.DomainFlowTmchUtils.NoMarksFoundMatchingDomainException;
import google.registry.flows.domain.DomainFlowTmchUtils.SignedMarkCertificateExpiredException; import google.registry.flows.domain.DomainFlowTmchUtils.SignedMarkCertificateExpiredException;
import google.registry.flows.domain.DomainFlowTmchUtils.SignedMarkCertificateInvalidException; import google.registry.flows.domain.DomainFlowTmchUtils.SignedMarkCertificateInvalidException;
@ -1301,7 +1303,7 @@ public class DomainApplicationCreateFlowTest
clock.setTo(DateTime.parse("2013-08-09T10:05:59Z").minusSeconds(1)); clock.setTo(DateTime.parse("2013-08-09T10:05:59Z").minusSeconds(1));
persistContactsAndHosts(); persistContactsAndHosts();
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = assertThrows(NoMarksFoundMatchingDomainException.class, this::runFlow); EppException thrown = assertThrows(FoundMarkNotYetValidException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
} }
@ -1311,7 +1313,7 @@ public class DomainApplicationCreateFlowTest
clock.setTo(DateTime.parse("2017-07-23T22:00:00.000Z")); clock.setTo(DateTime.parse("2017-07-23T22:00:00.000Z"));
persistContactsAndHosts(); persistContactsAndHosts();
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = assertThrows(NoMarksFoundMatchingDomainException.class, this::runFlow); EppException thrown = assertThrows(FoundMarkExpiredException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
} }

View file

@ -68,6 +68,8 @@ import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.domain.DomainCreateFlow.DomainHasOpenApplicationsException; import google.registry.flows.domain.DomainCreateFlow.DomainHasOpenApplicationsException;
import google.registry.flows.domain.DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException; import google.registry.flows.domain.DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException;
import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException; import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException;
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkExpiredException;
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkNotYetValidException;
import google.registry.flows.domain.DomainFlowTmchUtils.NoMarksFoundMatchingDomainException; import google.registry.flows.domain.DomainFlowTmchUtils.NoMarksFoundMatchingDomainException;
import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException; import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException;
import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException; import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException;
@ -1820,6 +1822,28 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
} }
@Test
public void testFail_startDateSunriseRegistration_markNotYetValid() throws Exception {
createTld("tld", TldState.START_DATE_SUNRISE);
// If we move now back in time a bit, the mark will not have gone into effect yet.
clock.setTo(DateTime.parse("2013-08-09T10:05:59Z").minusSeconds(1));
setEppInput("domain_create_registration_start_date_sunrise_encoded_signed_mark.xml");
persistContactsAndHosts();
EppException thrown = assertThrows(FoundMarkNotYetValidException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
public void testFail_startDateSunriseRegistration_markExpired() throws Exception {
createTld("tld", TldState.START_DATE_SUNRISE);
// Move time forward to the mark expiration time.
clock.setTo(DateTime.parse("2017-07-23T22:00:00.000Z"));
setEppInput("domain_create_registration_start_date_sunrise_encoded_signed_mark.xml");
persistContactsAndHosts();
EppException thrown = assertThrows(FoundMarkExpiredException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test @Test
public void testFailure_startDateSunriseRegistration_withClaimsNotice() throws Exception { public void testFailure_startDateSunriseRegistration_withClaimsNotice() throws Exception {
createTld("tld", TldState.START_DATE_SUNRISE); createTld("tld", TldState.START_DATE_SUNRISE);