mirror of
https://github.com/google/nomulus
synced 2026-07-07 08:36:50 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 514d24ed67 | |||
| c6868b771b | |||
| f34aec8b56 | |||
| b27b077638 | |||
| 0e8cd75a58 |
@@ -15,6 +15,7 @@
|
||||
package google.registry.export;
|
||||
|
||||
import static com.google.common.base.Verify.verifyNotNull;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
@@ -75,7 +76,8 @@ public class ExportDomainListsAction implements Runnable {
|
||||
ON d.repo_id = gp.domain_repo_id
|
||||
WHERE d.tld = :tld
|
||||
AND d.deletion_time > CAST(:now AS timestamptz)
|
||||
ORDER BY d.domain_name""";
|
||||
ORDER BY d.domain_name
|
||||
""";
|
||||
|
||||
// This may be a CSV, but it is uses a .txt file extension for back-compatibility
|
||||
static final String REGISTERED_DOMAINS_FILENAME_FORMAT = "registered_domains_%s.txt";
|
||||
@@ -93,10 +95,7 @@ public class ExportDomainListsAction implements Runnable {
|
||||
logger.atInfo().log("Exporting domain lists for TLDs %s.", realTlds);
|
||||
|
||||
boolean includeDeletionTimes =
|
||||
tm().transact(
|
||||
() ->
|
||||
FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, false));
|
||||
tm().transact(() -> FeatureFlag.isActiveNow(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS));
|
||||
realTlds.forEach(
|
||||
tld -> {
|
||||
List<String> domainsList =
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy;
|
||||
import static google.registry.model.EppResourceUtils.createRepoId;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -29,8 +30,10 @@ import google.registry.flows.FlowModule.RegistrarId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactCommand.Create;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
@@ -47,6 +50,7 @@ import org.joda.time.DateTime;
|
||||
* An EPP flow that creates a new contact.
|
||||
*
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link ResourceAlreadyExistsForThisClientException}
|
||||
* @error {@link ResourceCreateContentionException}
|
||||
* @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException}
|
||||
@@ -69,6 +73,9 @@ public final class ContactCreateFlow implements MutatingFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
Create command = (Create) resourceCommand;
|
||||
DateTime now = tm().getTransactionTime();
|
||||
verifyResourceDoesNotExist(Contact.class, targetId, now, registrarId);
|
||||
|
||||
@@ -24,6 +24,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -35,7 +36,9 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactCommand.Update;
|
||||
import google.registry.model.contact.ContactCommand.Update.Change;
|
||||
@@ -55,6 +58,7 @@ import org.joda.time.DateTime;
|
||||
/**
|
||||
* An EPP flow that updates a contact.
|
||||
*
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
@@ -92,6 +96,9 @@ public final class ContactUpdateFlow implements MutatingFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
Update command = (Update) resourceCommand;
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Contact existingContact = loadAndVerifyExistence(Contact.class, targetId, now);
|
||||
|
||||
@@ -72,7 +72,9 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -147,6 +149,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
|
||||
* @error {@link BulkDomainRegisteredForTooManyYearsException}
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
@@ -194,6 +197,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link RegistrantProhibitedException}
|
||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
@@ -244,7 +248,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
verifyResourceDoesNotExist(Domain.class, targetId, now, registrarId);
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
String domainLabel = domainName.parts().getFirst();
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, tld, domainName);
|
||||
TldState tldState = tld.getTldState(now);
|
||||
|
||||
@@ -25,7 +25,7 @@ import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
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;
|
||||
@@ -75,11 +75,13 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
@@ -478,27 +480,37 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresentIfRequiredForDataset(
|
||||
/**
|
||||
* Enforces the presence/absence of contact data depending on the minimum data set migration
|
||||
* schedule.
|
||||
*/
|
||||
static void validateContactDataPresence(
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
// Contacts are not required once we have begun the migration to the minimum dataset
|
||||
return;
|
||||
}
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
throws RequiredParameterMissingException, ParameterValuePolicyErrorException {
|
||||
// TODO(b/353347632): Change these flag checks to a registry config check once minimum data set
|
||||
// migration is completed.
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
if (registrant.isPresent()) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
if (!contacts.isEmpty()) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
} else if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
|
||||
Set<Type> roles = new HashSet<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
roles.add(contact.getType());
|
||||
}
|
||||
if (!roles.contains(Type.ADMIN)) {
|
||||
throw new MissingAdminContactException();
|
||||
}
|
||||
if (!roles.contains(Type.TECH)) {
|
||||
throw new MissingTechnicalContactException();
|
||||
Set<Type> roles = new HashSet<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
roles.add(contact.getType());
|
||||
}
|
||||
if (!roles.contains(Type.ADMIN)) {
|
||||
throw new MissingAdminContactException();
|
||||
}
|
||||
if (!roles.contains(Type.TECH)) {
|
||||
throw new MissingTechnicalContactException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,8 +1054,7 @@ public class DomainFlowUtils {
|
||||
String tldStr = tld.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
command.getRegistrant(), command.getContacts());
|
||||
validateContactDataPresence(command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
@@ -1367,6 +1378,13 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Having a registrant is prohibited by registry policy. */
|
||||
static class RegistrantProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public RegistrantProhibitedException() {
|
||||
super("Having a registrant is prohibited by registry policy");
|
||||
}
|
||||
}
|
||||
|
||||
/** Admin contact is required. */
|
||||
static class MissingAdminContactException extends RequiredParameterMissingException {
|
||||
public MissingAdminContactException() {
|
||||
|
||||
@@ -30,6 +30,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactDataPresence;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
|
||||
@@ -37,11 +38,10 @@ import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAl
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresentIfRequiredForDataset;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -64,9 +64,11 @@ import google.registry.flows.custom.DomainUpdateFlowCustomLogic.BeforeSaveParame
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -130,6 +132,7 @@ import org.joda.time.DateTime;
|
||||
* @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link RegistrantProhibitedException}
|
||||
* @error {@link DomainFlowUtils.SecDnsAllUsageException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
@@ -304,11 +307,12 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
|
||||
private Optional<VKey<Contact>> determineUpdatedRegistrant(Change change, Domain domain)
|
||||
throws EppException {
|
||||
// During phase 1 of minimum dataset transition, allow registrant to be removed
|
||||
// During or after the minimum dataset transition, allow registrant to be removed.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
|| FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
throw new MissingRegistrantException();
|
||||
@@ -325,8 +329,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateContactDataPresence(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.flows.exceptions;
|
||||
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
|
||||
/** Having contacts is prohibited by registry policy */
|
||||
public class ContactsProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public ContactsProhibitedException() {
|
||||
super("Having contacts is prohibited by registry policy");
|
||||
}
|
||||
}
|
||||
@@ -62,11 +62,31 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
/** The names of the feature flags that can be individually set. */
|
||||
public enum FeatureName {
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED,
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS
|
||||
/** Feature flag name used for testing only. */
|
||||
TEST_FEATURE(FeatureStatus.INACTIVE),
|
||||
|
||||
/** If we're not requiring the presence of contact data on domain EPP commands. */
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL(FeatureStatus.INACTIVE),
|
||||
|
||||
/** If we're not permitting the presence of contact data on any EPP commands. */
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED(FeatureStatus.INACTIVE),
|
||||
|
||||
/**
|
||||
* If we're including the upcoming domain drop date in the exported list of registered domains.
|
||||
*/
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE);
|
||||
|
||||
private final FeatureStatus defaultStatus;
|
||||
|
||||
FeatureName(FeatureStatus defaultStatus) {
|
||||
this.defaultStatus = defaultStatus;
|
||||
}
|
||||
|
||||
FeatureStatus getDefaultStatus() {
|
||||
return this.defaultStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
@@ -155,24 +175,24 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return status.getValueAtTime(time);
|
||||
}
|
||||
|
||||
/** Returns if the flag is active, or the default value if the flag does not exist. */
|
||||
public static boolean isActiveNowOrElse(FeatureName featureName, boolean defaultValue) {
|
||||
tm().assertInTransaction();
|
||||
return CACHE
|
||||
.get(featureName)
|
||||
.map(flag -> flag.getStatus(tm().getTransactionTime()).equals(ACTIVE))
|
||||
.orElse(defaultValue);
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active now. */
|
||||
/**
|
||||
* Returns whether the flag is active now, or else the flag's default value if it doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveNow(FeatureName featureName) {
|
||||
tm().assertInTransaction();
|
||||
return isActiveAt(featureName, tm().getTransactionTime());
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active at a given time. */
|
||||
/**
|
||||
* Returns whether the flag is active at the given time, or else the flag's default value if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) {
|
||||
return FeatureFlag.get(featureName).getStatus(dateTime).equals(ACTIVE);
|
||||
tm().assertInTransaction();
|
||||
return CACHE
|
||||
.get(featureName)
|
||||
.map(flag -> flag.getStatus(dateTime).equals(ACTIVE))
|
||||
.orElse(featureName.getDefaultStatus().equals(ACTIVE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,7 +55,7 @@ import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for all requests.
|
||||
* Base RDAP action for all requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -138,7 +138,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
// RFC7480 4.2 - servers receiving an RDAP request return an entity with a Content-Type header
|
||||
// containing the RDAP-specific JSON media type.
|
||||
response.setContentType(RESPONSE_MEDIA_TYPE);
|
||||
// RDAP Technical Implementation Guide 1.13 - when responding to RDAP valid requests, we MUST
|
||||
// RDAP Technical Implementation Guide 1.14 - when responding to RDAP valid requests, we MUST
|
||||
// include the Access-Control-Allow-Origin, which MUST be "*" unless otherwise specified.
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
try {
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP autonomous system number requests.
|
||||
* RDAP action for RDAP autonomous system number requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -41,14 +41,13 @@ final class RdapDataStructures {
|
||||
// Conformance to RFC 9083
|
||||
jsonArray.add("rdap_level_0");
|
||||
|
||||
// Conformance to the RDAP Response Profile V2.1
|
||||
// Conformance to the RDAP Response Profile V2.2 (February 2024)
|
||||
// (see section 1.2)
|
||||
jsonArray.add("icann_rdap_response_profile_1");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.2 (February 2024)
|
||||
// (see section 1.3)
|
||||
jsonArray.add("icann_rdap_response_profile_0");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.1
|
||||
// (see section 1.14)
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_0");
|
||||
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_1");
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for domain requests. */
|
||||
/** RDAP action for domain requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domain/",
|
||||
|
||||
@@ -51,6 +51,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Comparator;
|
||||
@@ -60,17 +61,15 @@ import java.util.stream.Stream;
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for domain search requests.
|
||||
* RDAP action for domain search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domains",
|
||||
@@ -442,7 +441,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
replicaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jakarta.persistence.Query query =
|
||||
Query query =
|
||||
replicaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(queryBuilder.toString())
|
||||
|
||||
@@ -37,14 +37,12 @@ import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) requests. the ICANN operational
|
||||
* profile dictates that the "handle" for registrars is to be the IANA registrar ID:
|
||||
* RDAP action for entity (contact and registrar) requests. the ICANN operational profile dictates
|
||||
* that the "handle" for registrars is to be the IANA registrar ID:
|
||||
*
|
||||
* <p>2.8.3. Registries MUST support lookup for entities with the registrar role within other
|
||||
* objects using the handle (as described in 3.1.5 of RFC 9082). The handle of the entity with the
|
||||
* registrar role MUST be equal to IANA Registrar ID. The entity with the registrar role in the RDAP
|
||||
* response MUST contain a publicIDs member to identify the IANA Registrar ID from the IANA’s
|
||||
* Registrar ID registry. The type value of the publicID object MUST be equal to IANA Registrar ID.
|
||||
* <p>2.4.1.Registry RDAP servers MUST support Registrar object lookup using an entity path request
|
||||
* for entities with the registrar role using the handle (as described in 3.1.5 of RFC9082) where
|
||||
* the handle of the entity with the registrar role is be [sic] equal to the IANA Registrar ID.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
@@ -104,7 +102,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
// Note we don't do RFC7480 5.3 - returning a different code if we wish to say "this info
|
||||
// exists but we don't want to show it to you", because we DON'T wish to say that.
|
||||
// exists, but we don't want to show it to you", because we DON'T wish to say that.
|
||||
throw new NotFoundException(pathSearchString + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) search requests.
|
||||
* RDAP action for entity (contact and registrar) search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
|
||||
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
|
||||
@@ -76,8 +76,6 @@ import java.util.Optional;
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/entities",
|
||||
|
||||
@@ -28,7 +28,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for help requests. */
|
||||
/** RDAP action for help requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = RdapHelpAction.PATH,
|
||||
|
||||
@@ -22,26 +22,18 @@ import google.registry.rdap.RdapDataStructures.Remark;
|
||||
/**
|
||||
* This file contains boilerplate required by the ICANN RDAP Profile.
|
||||
*
|
||||
* @see <a href="https://www.icann.org/resources/pages/rdap-operational-profile-2016-07-26-en">RDAP
|
||||
* Operational Profile for gTLD Registries and Registrars</a>
|
||||
* @see <a
|
||||
* href="https://itp.cdn.icann.org/en/files/registry-operators/rdap-response-profile-21feb24-en.pdf">
|
||||
* RDAP Response Profile</a>
|
||||
*/
|
||||
public class RdapIcannStandardInformation {
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.4.10. */
|
||||
private static final Notice CONFORMANCE_NOTICE =
|
||||
Notice.builder()
|
||||
.setDescription(
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and"
|
||||
+ " Registrars version 1.0")
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.5.18. */
|
||||
/** Required by RDAP Response Profile section 2.6.3. */
|
||||
private static final Notice DOMAIN_STATUS_CODES_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Status Codes")
|
||||
.setDescription(
|
||||
"For more information on domain status codes, please visit"
|
||||
+ " https://icann.org/epp")
|
||||
"For more information on domain status codes, please visit https://icann.org/epp")
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("glossary")
|
||||
@@ -50,7 +42,7 @@ public class RdapIcannStandardInformation {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Response Profile section 2.11. */
|
||||
/** Required by RDAP Response Profile section 2.10. */
|
||||
private static final Notice INACCURACY_COMPLAINT_FORM_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("RDDS Inaccuracy Complaint Form")
|
||||
@@ -79,28 +71,16 @@ public class RdapIcannStandardInformation {
|
||||
/** Boilerplate notices required by domain responses. */
|
||||
static final ImmutableList<Notice> DOMAIN_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(
|
||||
CONFORMANCE_NOTICE,
|
||||
// RDAP Response Profile 2.6.3
|
||||
DOMAIN_STATUS_CODES_NOTICE,
|
||||
// RDAP Response Profile 2.11
|
||||
// RDAP Response Profile 2.10
|
||||
INACCURACY_COMPLAINT_FORM_NOTICE);
|
||||
|
||||
/** Boilerplate notice for when a domain is blocked by BSA. */
|
||||
static final ImmutableList<Notice> DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(DOMAIN_BLOCKED_BY_BSA_NOTICE);
|
||||
|
||||
/** Boilerplate remarks required by nameserver and entity responses. */
|
||||
static final ImmutableList<Notice> NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(CONFORMANCE_NOTICE);
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.9, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.6.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.6. */
|
||||
static final Remark SUMMARY_DATA_REMARK =
|
||||
Remark.builder()
|
||||
.setTitle("Incomplete Data")
|
||||
@@ -109,14 +89,7 @@ public class RdapIcannStandardInformation {
|
||||
.setType(Remark.Type.OBJECT_TRUNCATED_UNEXPLAINABLE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.8, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.5.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.5. */
|
||||
static final Notice TRUNCATED_RESULT_SET_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Search Policy")
|
||||
@@ -148,7 +121,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included when requester is not logged in as the owner of the contact being returned.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.4.3.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
|
||||
Remark.builder()
|
||||
@@ -169,10 +144,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included in ALL contact responses, even if the user is authorized.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.5.3.
|
||||
*
|
||||
* <p>NOTE that unlike other redacted fields, there's no allowance to give the email to authorized
|
||||
* users or allow for registrar consent.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_EMAIL_REDACTED_FOR_DOMAIN =
|
||||
Remark.builder()
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP IP address requests.
|
||||
* RDAP action for RDAP IP address requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -221,7 +221,7 @@ public class RdapJsonFormatter {
|
||||
* Map of EPP event values to the RDAP equivalents.
|
||||
*
|
||||
* <p>Only has entries for optional events, either stated as optional in the RDAP Response Profile
|
||||
* 15feb19, or not mentioned at all but thought to be useful anyway.
|
||||
* section 2.3.2, or not mentioned at all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
@@ -292,8 +292,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Domain}.
|
||||
*
|
||||
* <p>NOTE that domain searches aren't in the spec yet - they're in the RFC 9082 that describes
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide 15feb19, meaning
|
||||
* we don't have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide, meaning we don't
|
||||
* have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
*
|
||||
* <p>We're implementing domain searches anyway, BUT we won't have the response for searches
|
||||
* conform to the RDAP Response Profile.
|
||||
@@ -307,9 +307,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType != OutputDataType.FULL) {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.1 discusses the domain name.
|
||||
// RDAP Response Profile section 2.1 discusses the domain name.
|
||||
builder.setLdhName(domain.getDomainName());
|
||||
// RDAP Response Profile 15feb19 section 2.2:
|
||||
// RDAP Response Profile section 2.2:
|
||||
// The domain handle MUST be the ROID
|
||||
builder.setHandle(domain.getRepoId());
|
||||
// If this is a summary (search result) - we'll return now. Since there's no requirement for
|
||||
@@ -317,9 +317,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType == OutputDataType.SUMMARY) {
|
||||
return builder.build();
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.1:
|
||||
// RDAP Response Profile section 2.3.1:
|
||||
// The domain object in the RDAP response MUST contain the following events:
|
||||
// [registration, expiration, last update of RDAP database]
|
||||
// [registration, expiration]
|
||||
builder
|
||||
.eventsBuilder()
|
||||
.add(
|
||||
@@ -333,14 +333,18 @@ public class RdapJsonFormatter {
|
||||
.setEventAction(EventAction.EXPIRATION)
|
||||
.setEventDate(domain.getRegistrationExpirationTime())
|
||||
.build(),
|
||||
// RDAP response profile section 1.5:
|
||||
// The topmost object in the RDAP response MUST contain an event of "eventAction" type
|
||||
// "last update of RDAP database" with a value equal to the timestamp when the RDAP
|
||||
// database was last updated
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
// RDAP Response Profile 15feb19 section 2.3.2 discusses optional events. We add some of those
|
||||
// RDAP Response Profile section 2.3.2 discusses optional events. We add some of those
|
||||
// here. We also add a few others we find interesting.
|
||||
builder.eventsBuilder().addAll(makeOptionalEvents(domain));
|
||||
// RDAP Response Profile 15feb19 section 2.4.1:
|
||||
// RDAP Response Profile section 2.4.1:
|
||||
// The domain object in the RDAP response MUST contain an entity with the Registrar role.
|
||||
//
|
||||
// See {@link createRdapRegistrarEntity} for details of section 2.4 conformance
|
||||
@@ -378,8 +382,6 @@ public class RdapJsonFormatter {
|
||||
// RDAP Response Profile 2.6.3, must have a notice about statuses. That is in {@link
|
||||
// RdapIcannStandardInformation#domainBoilerplateNotices}
|
||||
|
||||
// Kick off the database loads of the nameservers that we will need, so it can load
|
||||
// asynchronously while we load and process the contacts.
|
||||
ImmutableSet<Host> loadedHosts =
|
||||
replicaTm()
|
||||
.transact(
|
||||
@@ -424,12 +426,12 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
// RDAP Response Profile 2.8: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(host, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.10 - MUST contain a secureDns member including at least a
|
||||
// RDAP Response Profile 2.9 - MUST contain a secureDns member including at least a
|
||||
// delegationSigned element. Other elements (e.g. dsData) MUST be included if the domain name is
|
||||
// signed and the elements are stored in the Registry
|
||||
//
|
||||
@@ -454,13 +456,13 @@ public class RdapJsonFormatter {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
// We need the ldhName: RDAP Response Profile 2.9.1, 4.1
|
||||
// We need the ldhName: RDAP Response Profile 2.8.1, 4.1
|
||||
builder.setLdhName(host.getHostName());
|
||||
// Handle is optional, but if given it MUST be the ROID.
|
||||
// We will set it always as it's important as a "self link"
|
||||
builder.setHandle(host.getRepoId());
|
||||
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.9.2
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.8.2
|
||||
// It isn't mentioned at all anywhere else. So we can just not put it at all?
|
||||
//
|
||||
// To be safe, we'll put it on the "FULL" version anyway
|
||||
@@ -492,7 +494,7 @@ public class RdapJsonFormatter {
|
||||
|
||||
// For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2.
|
||||
//
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.9.2
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.8.2
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
for (InetAddress inetAddress : host.getInetAddresses()) {
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
@@ -510,7 +512,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().add(createRdapRegistrarEntity(registrar, OutputDataType.INTERNAL));
|
||||
}
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 4.4, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
Event.builder()
|
||||
@@ -535,10 +537,7 @@ public class RdapJsonFormatter {
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
@@ -578,7 +577,7 @@ public class RdapJsonFormatter {
|
||||
.add(RdapIcannStandardInformation.CONTACT_EMAIL_REDACTED_FOR_DOMAIN);
|
||||
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 2.7.6 must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5 must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects. I'm not sure why it's in that
|
||||
// section at all...
|
||||
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -656,8 +655,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Registrar}.
|
||||
*
|
||||
* <p>This object can be INTERNAL to the Domain and Nameserver responses, with requirements
|
||||
* discussed in the RDAP Response Profile 15feb19 sections 2.4 (internal to Domain) and 4.3
|
||||
* (internal to Namesever)
|
||||
* discussed in the RDAP Response Profile sections 2.4 (internal to Domain) and 4.3 (internal to
|
||||
* Namesever)
|
||||
*
|
||||
* @param registrar the registrar object from which the RDAP response
|
||||
* @param outputDataType whether to generate FULL, SUMMARY, or INTERNAL data.
|
||||
@@ -721,6 +720,15 @@ public class RdapJsonFormatter {
|
||||
builder.linksBuilder().add(makeSelfLink("entity", ianaIdentifier.toString()));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.4.6: must have a links entry pointing to the registrar URL, with a
|
||||
// rel:about and a value containing the registrar RDAP base URL (if present)
|
||||
if (registrar.getUrl() != null) {
|
||||
Link.Builder registrarLinkBuilder =
|
||||
Link.builder().setHref(registrar.getUrl()).setRel("about").setType("text/html");
|
||||
registrar.getRdapBaseUrls().stream().findFirst().ifPresent(registrarLinkBuilder::setValue);
|
||||
builder.linksBuilder().add(registrarLinkBuilder.build());
|
||||
}
|
||||
|
||||
// There's no mention of the registrar STATUS in the RDAP Response Profile, so we'll only add it
|
||||
// for FULL response
|
||||
// We could probably not add it at all, but it could be useful for us internally
|
||||
@@ -746,7 +754,6 @@ public class RdapJsonFormatter {
|
||||
//
|
||||
// Write the minimum, meaning only ABUSE for INTERNAL registrars, nothing for SUMMARY and
|
||||
// everything for FULL.
|
||||
//
|
||||
if (outputDataType != OutputDataType.SUMMARY) {
|
||||
ImmutableList<RdapContactEntity> registrarContacts =
|
||||
registrar.getContactsFromReplica().stream()
|
||||
@@ -767,7 +774,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().addAll(registrarContacts);
|
||||
}
|
||||
|
||||
// Rdap Response Profile 3.3, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -925,8 +932,8 @@ public class RdapJsonFormatter {
|
||||
* Creates the list of optional events to list in domain, nameserver, or contact replies.
|
||||
*
|
||||
* <p>Only has entries for optional events that won't be shown in "SUMMARY" versions of these
|
||||
* objects. These are either stated as optional in the RDAP Response Profile 15feb19, or not
|
||||
* mentioned at all but thought to be useful anyway.
|
||||
* objects. These are either stated as optional in the RDAP Response Profile, or not mentioned at
|
||||
* all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
@@ -965,7 +972,7 @@ public class RdapJsonFormatter {
|
||||
lastChangeTime = modificationTime;
|
||||
}
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.2.2:
|
||||
// RDAP Response Profile section 2.3.2.2:
|
||||
// The event of eventAction type last changed MUST be omitted if the domain name has not been
|
||||
// updated since it was created
|
||||
if (lastChangeTime.isAfter(creationTime)) {
|
||||
@@ -982,7 +989,7 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a vCard address entry: array of strings specifying the components of the address.
|
||||
*
|
||||
* <p>Rdap Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* <p>RDAP Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* Response Profile 3.1.2: optional fields: State/Province, Postal Code, Fax Number
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7095">RFC 7095: jCard: The JSON Format for
|
||||
|
||||
@@ -33,7 +33,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for nameserver requests. */
|
||||
/** RDAP action for nameserver requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/nameserver/",
|
||||
@@ -48,7 +48,7 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
|
||||
@Override
|
||||
public RdapNameserver getJsonObjectForResource(String pathSearchString, boolean isHeadRequest) {
|
||||
// RDAP Technical Implementation Guide 2.2.1 - we must support A-label (Punycode) and U-label
|
||||
// RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label
|
||||
// (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both.
|
||||
pathSearchString = canonicalizeName(pathSearchString);
|
||||
// The RDAP syntax is /rdap/nameserver/ns1.mydomain.com.
|
||||
|
||||
@@ -47,9 +47,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for nameserver search requests.
|
||||
* RDAP action for nameserver search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
|
||||
@@ -45,11 +45,7 @@ import java.util.Optional;
|
||||
/** Object Classes defined in RFC 9083 section 5. */
|
||||
final class RdapObjectClasses {
|
||||
|
||||
/**
|
||||
* Temporary implementation of VCards.
|
||||
*
|
||||
* <p>Will create a better implementation soon.
|
||||
*/
|
||||
/** Rough implementation of VCards. */
|
||||
@RestrictJsonNames({})
|
||||
@AutoValue
|
||||
public abstract static class Vcard implements Jsonable {
|
||||
@@ -140,8 +136,8 @@ final class RdapObjectClasses {
|
||||
public enum BoilerplateType {
|
||||
DOMAIN(RdapIcannStandardInformation.DOMAIN_BOILERPLATE_NOTICES),
|
||||
DOMAIN_BLOCKED_BY_BSA(RdapIcannStandardInformation.DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
ENTITY(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(ImmutableList.of()),
|
||||
ENTITY(ImmutableList.of()),
|
||||
OTHER(ImmutableList.of());
|
||||
|
||||
@SuppressWarnings("ImmutableEnumChecker") // immutable lists are, in fact, immutable
|
||||
@@ -173,8 +169,8 @@ final class RdapObjectClasses {
|
||||
* The Top Level JSON reply, Adds the required top-level boilerplate to a ReplyPayloadBase.
|
||||
*
|
||||
* <p>RFC 9083 specifies that the top-level object should include an entry indicating the
|
||||
* conformance level. ICANN RDAP spec for 15feb19 mandates several additional entries, in sections
|
||||
* 2.6.3, 2.11 of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
* conformance level. The RDAP spec mandates several additional entries, in sections 2.6.3, 2.10
|
||||
* of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
*/
|
||||
@AutoValue
|
||||
@RestrictJsonNames({})
|
||||
@@ -353,7 +349,7 @@ final class RdapObjectClasses {
|
||||
*
|
||||
* <p>Takes care of the name and unicode field.
|
||||
*
|
||||
* <p>See RDAP Response Profile 15feb19 sections 2.1 and 4.1.
|
||||
* <p>See RDAP Response Profile sections 2.1 and 4.1.
|
||||
*
|
||||
* <p>Note the ldhName field is only required for non-IDN names or IDN names when the query was an
|
||||
* A-label. It is optional for IDN names when the query was a U-label. Because we don't want to
|
||||
@@ -471,7 +467,7 @@ final class RdapObjectClasses {
|
||||
}
|
||||
|
||||
/**
|
||||
* an integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* An integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* DS record in the parent zone [RFC5910].
|
||||
*
|
||||
* <p>Note that although it isn't given as optional in RFC 9083, in RFC5910 it's mentioned as
|
||||
|
||||
@@ -44,7 +44,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for domain, nameserver and entity search requests.
|
||||
* Base RDAP action for domain, nameserver and entity search requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -155,7 +155,6 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
*/
|
||||
<T extends EppResource> RdapResultSet<T> getMatchingResources(
|
||||
CriteriaQueryBuilder<T> builder, boolean checkForVisibility, int querySizeLimit) {
|
||||
replicaTm().assertInTransaction();
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
builder =
|
||||
|
||||
@@ -39,8 +39,8 @@ public final class RdapSearchPattern {
|
||||
/**
|
||||
* Pattern for allowed LDH searches.
|
||||
*
|
||||
* <p>Based on RFC 9082 4.1. Must contains only alphanumeric plus dots and hyphens. A single
|
||||
* whildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* <p>Based on RFC 9082 4.1. Must contain only alphanumeric plus dots and hyphens. A single
|
||||
* wildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* (so exam* and exam*.com are allowed, but exam*le.com isn't allowd)
|
||||
*
|
||||
* <p>The prefix is in group(1), and the suffix without the dot (if it exists) is in group(4). If
|
||||
@@ -123,7 +123,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* @param searchQuery the string containing the partial match pattern
|
||||
* @throws UnprocessableEntityException if {@code pattern} does not meet the requirements of RFC
|
||||
@@ -150,7 +150,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH or Unicode format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* <p>In theory, according to RFC 9082 4.1 - we should make some checks about partial matching in
|
||||
* unicode queries. We don't, but we might want to just disable partial matches for unicode inputs
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class RdapUtils {
|
||||
*
|
||||
* <p>Used for RDAP Technical Implementation Guide 2.4.2 - search of registrar by the fn element.
|
||||
*
|
||||
* <p>For convenience, we use case insensitive search.
|
||||
* <p>For convenience, we use case-insensitive search.
|
||||
*/
|
||||
static Optional<Registrar> getRegistrarByName(String registrarName) {
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
@@ -36,7 +37,7 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.security.GeneralSecurityException;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@@ -72,7 +73,7 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
|
||||
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
|
||||
processAllRegistrars(ianaIdsToUrls);
|
||||
} catch (Exception e) {
|
||||
throw new InternalServerErrorException("Error when retrieving RDAP base URL CSV file", e);
|
||||
}
|
||||
@@ -80,7 +81,14 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private static void processAllRegistrars(ImmutableMap<String, String> ianaIdsToUrls) {
|
||||
int nonUpdatedRegistrars = 0;
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
// Split into multiple transactions to avoid load-save-reload conflicts. Re-building a registrar
|
||||
// requires a full (cached) load of all registrars to avoid IANA ID conflicts, so if multiple
|
||||
// registrars are modified in the same transaction, the second build call will fail.
|
||||
Iterable<Registrar> registrars =
|
||||
tm().transact(
|
||||
PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ,
|
||||
Registrar::loadAll);
|
||||
for (Registrar registrar : registrars) {
|
||||
// Only update REAL registrars
|
||||
if (registrar.getType() != Registrar.Type.REAL) {
|
||||
continue;
|
||||
@@ -100,7 +108,12 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
"Updating RDAP base URLs for registrar %s from %s to %s",
|
||||
registrar.getRegistrarId(), registrar.getRdapBaseUrls(), baseUrls);
|
||||
}
|
||||
tm().put(registrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Reload inside a transaction to avoid race conditions
|
||||
Registrar reloadedRegistrar = tm().loadByEntity(registrar);
|
||||
tm().put(reloadedRegistrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
});
|
||||
}
|
||||
}
|
||||
logger.atInfo().log("No change in RDAP base URLs for %d registrars", nonUpdatedRegistrars);
|
||||
@@ -108,9 +121,9 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private ImmutableMap<String, String> getIanaIdsToUrls()
|
||||
throws IOException, GeneralSecurityException {
|
||||
CSVParser csv;
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(new URL(RDAP_IDS_URL));
|
||||
// Explictly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
HttpURLConnection connection =
|
||||
urlConnectionService.createConnection(URI.create(RDAP_IDS_URL).toURL());
|
||||
// Explicitly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
// ICANN.
|
||||
connection.setRequestProperty(ACCEPT_ENCODING, "gzip");
|
||||
String csvString;
|
||||
@@ -128,11 +141,11 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
csv =
|
||||
CSVParser csv =
|
||||
CSVFormat.Builder.create(CSVFormat.DEFAULT)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.build()
|
||||
.get()
|
||||
.parse(new StringReader(csvString));
|
||||
ImmutableMap.Builder<String, String> result = new ImmutableMap.Builder<>();
|
||||
for (CSVRecord record : csv) {
|
||||
|
||||
@@ -16,6 +16,8 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
@@ -62,8 +64,8 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
protected void initMutatingEppToolCommand() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (!FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL, false)) {
|
||||
if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
&& !FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package google.registry.flows.contact;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.ContactSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
@@ -22,15 +25,19 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -89,6 +96,18 @@ class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, Cont
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_cannotCreateContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_resourceContention() throws Exception {
|
||||
String targetId = getUniqueIdFromCommand();
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package google.registry.flows.contact;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.ContactSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
@@ -22,10 +25,12 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
@@ -35,8 +40,10 @@ import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactAddress;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
@@ -86,6 +93,18 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_cannotUpdateContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_updatingInternationalizedPostalInfoDeletesLocalized() throws Exception {
|
||||
Contact contact =
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.model.billing.BillingBase.Flag.SUNRISE;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
||||
@@ -133,6 +134,7 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTl
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||
@@ -145,6 +147,7 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
@@ -247,12 +250,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"badcrash,NAME_COLLISION"),
|
||||
persistReservedList("global-list", "resdom,FULLY_BLOCKED"))
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY));
|
||||
}
|
||||
|
||||
@@ -2104,8 +2101,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2115,6 +2112,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_noRegistrantButSomeOtherContactTypes() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_registrant.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingAdmin() {
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
@@ -2126,8 +2137,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2137,6 +2148,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantAndOtherContactsSent() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingTech() {
|
||||
setEppInput("domain_create_missing_tech.xml");
|
||||
@@ -2148,8 +2173,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2170,8 +2195,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingNonRegistrantContacts() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2181,6 +2206,34 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantNotPermitted() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_non_registrant_contacts.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase2_noContactsWhatsoever() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_no_contacts.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badIdn() {
|
||||
createTld("xn--q9jyb4c");
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
|
||||
@@ -89,10 +90,12 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedForTldE
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.SecDnsAllUsageException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UrgentAttributeNotSupportedException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
@@ -146,12 +149,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
createTld("tld");
|
||||
// Note that "domain_update.xml" tests adding and removing the same contact type.
|
||||
setEppInput("domain_update.xml");
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void persistReferencedEntities() {
|
||||
@@ -336,8 +333,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_emptyRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -348,6 +345,24 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(reloadResourceByForeignKey().getRegistrant()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_nonRegistrantContactsStillExist() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_empty_registrant.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
// Fails because after the update the domain would still have some contacts on it even though
|
||||
// the registrant has been removed.
|
||||
ContactsProhibitedException thrown =
|
||||
assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
private void modifyDomainToHave13Nameservers() throws Exception {
|
||||
ImmutableSet.Builder<VKey<Host>> nameservers = new ImmutableSet.Builder<>();
|
||||
for (int i = 1; i < 15; i++) {
|
||||
@@ -1540,8 +1555,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -1558,6 +1573,29 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantStillExists() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_admin.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
RegistrantProhibitedException thrown =
|
||||
assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeTech() throws Exception {
|
||||
setEppInput("domain_update_remove_tech.xml");
|
||||
@@ -1577,8 +1615,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -1595,6 +1633,30 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase2_removeAllContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_all_contacts.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
Domain updatedDomain = reloadResourceByForeignKey();
|
||||
assertThat(updatedDomain.getRegistrant()).isEmpty();
|
||||
assertThat(updatedDomain.getContacts()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_addPendingDeleteContact() throws Exception {
|
||||
persistReferencedEntities();
|
||||
|
||||
@@ -56,6 +56,7 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
persistResource(featureFlag);
|
||||
FeatureFlag flagFromDb = loadByEntity(featureFlag);
|
||||
assertThat(featureFlag).isEqualTo(flagFromDb);
|
||||
assertThat(featureFlag.getFeatureName()).isEqualTo(TEST_FEATURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -217,23 +218,12 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
.build());
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isFalse();
|
||||
});
|
||||
fakeClock.advanceBy(Duration.standardDays(365));
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isTrue();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_default_doesNotExist() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue();
|
||||
assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isTrue();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -379,6 +379,7 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setPassword("foo-BAR2")
|
||||
.setPhoneNumber("+1.3334445555")
|
||||
.setPhonePasscode("12345")
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.newregistrar.com/"))
|
||||
.setRegistryLockAllowed(false)
|
||||
.build();
|
||||
}
|
||||
@@ -393,6 +394,7 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setPassword("password2")
|
||||
.setPhoneNumber("+1.2223334444")
|
||||
.setPhonePasscode("22222")
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.theregistrar.com/"))
|
||||
.setRegistryLockAllowed(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -205,16 +205,6 @@ have failed to comply with these terms.",
|
||||
}
|
||||
JsonArray notices = jsonObject.getAsJsonArray("notices");
|
||||
notices.add(createTosNotice());
|
||||
notices.add(
|
||||
JsonParser.parseString(
|
||||
"""
|
||||
{
|
||||
"description": [
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars \
|
||||
version 1.0"
|
||||
]
|
||||
}
|
||||
"""));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,13 @@ final class RdapDataStructuresTest {
|
||||
@Test
|
||||
void testRdapConformance() {
|
||||
assertThat(RdapConformance.INSTANCE.toJson())
|
||||
.isEqualTo(createJson(
|
||||
"[",
|
||||
" 'rdap_level_0',",
|
||||
" 'icann_rdap_response_profile_0',",
|
||||
" 'icann_rdap_technical_implementation_guide_0'",
|
||||
"]"));
|
||||
.isEqualTo(
|
||||
createJson(
|
||||
"[",
|
||||
" 'rdap_level_0',",
|
||||
" 'icann_rdap_response_profile_1',",
|
||||
" 'icann_rdap_technical_implementation_guide_1'",
|
||||
"]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -84,9 +84,7 @@ public final class FullFieldsTestEntityHelper {
|
||||
.setFaxNumber("+1.2125551213")
|
||||
.setEmailAddress("contact-us@example.com")
|
||||
.setWhoisServer("whois.example.com")
|
||||
.setRdapBaseUrls(
|
||||
ImmutableSet.of(
|
||||
"https://rdap.example.com/withSlash/", "https://rdap.example.com/withoutSlash"))
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.example.com/withSlash/"))
|
||||
.setUrl("http://my.fake.url")
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
@@ -115,7 +116,7 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimal() throws Exception {
|
||||
void testSuccess_minimumDatasetPhase1_noContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
@@ -123,6 +124,19 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, ACTIVE))
|
||||
.build());
|
||||
// Test that each optional field can be omitted. Also tests the auto-gen password.
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
eppVerifier.verifySent("domain_create_minimal.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase2_noContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, ACTIVE))
|
||||
.build());
|
||||
// Test that each optional field can be omitted. Also tests the auto-gen password.
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"example.tld");
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
<domain:hostObj>ns2.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">24.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.024Z">100.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.023Z">100.00</fee:fee>
|
||||
</fee:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">200.00</fee:fee>
|
||||
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.028Z">100.00
|
||||
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.027Z">100.00
|
||||
</fee:fee>
|
||||
</fee:creData>
|
||||
</extension>
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add/>
|
||||
<domain:rem>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="billing">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
</domain:rem>
|
||||
<domain:chg>
|
||||
<domain:registrant/>
|
||||
</domain:chg>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "entity",
|
||||
"handle": "%CONTACT_HANDLE_1%",
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "%CONTACT_HANDLE_1%",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "%CONTACT_HANDLE_1%",
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"status": [
|
||||
"client delete prohibited",
|
||||
@@ -25,12 +25,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -122,6 +116,12 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"entities": [
|
||||
@@ -15,6 +15,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/addgraceperiod.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
@@ -128,6 +134,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/addgraceperiod.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.theregistrar.com/domain/addgraceperiod.lol",
|
||||
"rel": "related",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/addgraceperiod.lol"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
|
||||
+14
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"entities": [
|
||||
@@ -15,6 +15,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/autorenew.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
@@ -132,6 +138,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/autorenew.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.theregistrar.com/domain/autorenew.lol",
|
||||
"rel": "related",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/autorenew.lol"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
@@ -25,12 +25,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -105,6 +99,12 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"ldhName": "%DOMAIN_PUNYCODE_NAME_1%",
|
||||
@@ -26,12 +26,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -117,6 +111,12 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
|
||||
+14
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"entities": [
|
||||
@@ -15,6 +15,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/renew.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
@@ -128,6 +134,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/renew.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.theregistrar.com/domain/renew.lol",
|
||||
"rel": "related",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/renew.lol"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
|
||||
+8
-8
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
@@ -25,12 +25,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -112,6 +106,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
|
||||
+8
-8
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
@@ -25,12 +25,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -112,6 +106,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/cat.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
|
||||
+14
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"entities": [
|
||||
@@ -15,6 +15,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/redemption.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
@@ -128,6 +134,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/redemption.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.theregistrar.com/domain/redemption.lol",
|
||||
"rel": "related",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/redemption.lol"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
|
||||
+8
-8
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
@@ -25,12 +25,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -112,6 +106,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
|
||||
+14
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"entities": [
|
||||
@@ -15,6 +15,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/transfer.lol"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
@@ -128,6 +134,12 @@
|
||||
"rel": "self",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/transfer.lol"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.theregistrar.com/domain/transfer.lol",
|
||||
"rel": "related",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domain/transfer.lol"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"status": [
|
||||
"client delete prohibited",
|
||||
@@ -26,12 +26,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -115,6 +109,12 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
|
||||
+8
-8
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
@@ -26,12 +26,6 @@
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -115,6 +109,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/domains"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -140,12 +140,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title" : "Status Codes",
|
||||
"description" :
|
||||
|
||||
+2
-8
@@ -84,8 +84,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -120,12 +120,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title" : "Status Codes",
|
||||
"description" :
|
||||
|
||||
+2
-8
@@ -84,8 +84,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -141,12 +141,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title" : "Status Codes",
|
||||
"description" :
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"domainSearchResults": [
|
||||
{
|
||||
@@ -82,9 +82,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": ["This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"]
|
||||
},
|
||||
{
|
||||
"title": "Status Codes",
|
||||
"description": ["For more information on domain status codes, please visit https://icann.org/epp"],
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_1%",
|
||||
@@ -39,6 +39,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"status": [
|
||||
"active"
|
||||
@@ -36,6 +36,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_1%",
|
||||
@@ -42,6 +42,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_1%",
|
||||
@@ -40,6 +40,12 @@
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.theregistrar.com/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -112,12 +112,6 @@
|
||||
],
|
||||
"title":"RDAP Terms of Service"
|
||||
},
|
||||
{
|
||||
"description":
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
[
|
||||
@@ -150,7 +144,7 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -82,11 +82,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"Status Codes",
|
||||
"description":[
|
||||
@@ -116,7 +111,7 @@
|
||||
],
|
||||
"rdapConformance":[
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -134,12 +134,6 @@
|
||||
"type": "text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -153,12 +153,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
],
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -106,12 +106,6 @@
|
||||
"value": "https://example.tld/rdap/nameservers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -232,8 +232,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -267,12 +267,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -127,12 +127,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title" : "Status Codes",
|
||||
"description" :
|
||||
|
||||
@@ -136,8 +136,8 @@
|
||||
],
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -171,12 +171,6 @@
|
||||
"value": "https://example.tld/rdap/nameservers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"href": "https://example.tld/rdap/entity/301",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
@@ -69,6 +75,12 @@
|
||||
"href": "https://example.tld/rdap/entity/302",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
@@ -127,6 +139,12 @@
|
||||
"href": "https://example.tld/rdap/entity/303",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
@@ -185,6 +203,12 @@
|
||||
"href": "https://example.tld/rdap/entity/304",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
@@ -236,8 +260,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices":
|
||||
[
|
||||
@@ -271,12 +295,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "%REGISTRAR_HANDLE_1%",
|
||||
@@ -15,6 +15,12 @@
|
||||
"href": "https://example.tld/rdap/entity/%REGISTRAR_HANDLE_1%",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "%REGISTRAR_HANDLE_1%",
|
||||
"links" : [
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"status" : ["%STATUS_1%"],
|
||||
"roles" : ["registrar"],
|
||||
"events": [
|
||||
|
||||
@@ -232,8 +232,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices":
|
||||
[
|
||||
@@ -288,12 +288,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@
|
||||
],
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -192,12 +192,6 @@
|
||||
"value": "https://example.tld/rdap/nameservers"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@
|
||||
"links" :
|
||||
[
|
||||
{
|
||||
"rel" : "self",
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/entity/0002-ROID",
|
||||
"type" : "application/rdap+json",
|
||||
"type": "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
],
|
||||
@@ -182,6 +182,12 @@
|
||||
"href": "https://example.tld/rdap/entity/301",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
@@ -233,8 +239,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -289,12 +295,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"href": "https://example.tld/rdap/entity/301",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
@@ -69,6 +75,12 @@
|
||||
"href": "https://example.tld/rdap/entity/302",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
@@ -127,6 +139,12 @@
|
||||
"href": "https://example.tld/rdap/entity/303",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
@@ -185,6 +203,12 @@
|
||||
"href": "https://example.tld/rdap/entity/304",
|
||||
"type" : "application/rdap+json",
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
@@ -236,8 +260,8 @@
|
||||
],
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
@@ -292,12 +316,6 @@
|
||||
"value": "https://example.tld/rdap/entities"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description" :
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"key":"value","rdapConformance":["rdap_level_0","icann_rdap_response_profile_0","icann_rdap_technical_implementation_guide_0"],"notices":[{"title":"RDAP Terms of Service","links":[{"href":"https:\/\/www.example.tld\/about\/rdap\/tos.html","rel":"alternate","type":"text\/html","value":"http:\/\/myserver.example.com\/help\/tos"}],"description":["By querying our Domain Database, you are agreeing to comply with these terms so please read them carefully.","Any information provided is 'as is' without any guarantee of accuracy.","Please do not misuse the Domain Database. It is intended solely for query-based access.","Don't use the Domain Database to allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations.","Don't access our Domain Database through the use of high volume, automated electronic processes that send queries or data to the systems of any ICANN-accredited registrar.","You may only use the information contained in the Domain Database for lawful purposes.","Do not compile, repackage, disseminate, or otherwise use the information contained in the Domain Database in its entirety, or in any substantial portion, without our prior written permission.","We may retain certain details about queries to our Domain Database for the purposes of detecting and preventing misuse.","We reserve the right to restrict or deny your access to the database if we suspect that you have failed to comply with these terms.","We reserve the right to modify this agreement at any time."]}]}
|
||||
{"key":"value","rdapConformance":["rdap_level_0","icann_rdap_response_profile_1","icann_rdap_technical_implementation_guide_1"],"notices":[{"title":"RDAP Terms of Service","links":[{"href":"https:\/\/www.example.tld\/about\/rdap\/tos.html","rel":"alternate","type":"text\/html","value":"http:\/\/myserver.example.com\/help\/tos"}],"description":["By querying our Domain Database, you are agreeing to comply with these terms so please read them carefully.","Any information provided is 'as is' without any guarantee of accuracy.","Please do not misuse the Domain Database. It is intended solely for query-based access.","Don't use the Domain Database to allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations.","Don't access our Domain Database through the use of high volume, automated electronic processes that send queries or data to the systems of any ICANN-accredited registrar.","You may only use the information contained in the Domain Database for lawful purposes.","Do not compile, repackage, disseminate, or otherwise use the information contained in the Domain Database in its entirety, or in any substantial portion, without our prior written permission.","We may retain certain details about queries to our Domain Database for the purposes of detecting and preventing misuse.","We reserve the right to restrict or deny your access to the database if we suspect that you have failed to comply with these terms.","We reserve the right to modify this agreement at any time."]}]}
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
"rel" : "related",
|
||||
"href" : "https://rdap.example.com/withSlash/domain/cat.xn--q9jyb4c",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel" : "related",
|
||||
"href" : "https://rdap.example.com/withoutSlash/domain/cat.xn--q9jyb4c",
|
||||
"type" : "application/rdap+json"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -112,6 +107,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
"href": "https://rdap.example.com/withSlash/domain/cat.xn--q9jyb4c",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/cat.xn--q9jyb4c",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -109,6 +104,12 @@
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/entity/1",
|
||||
"type": "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds": [
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
"href": "https://rdap.example.com/withSlash/domain/fish.xn--q9jyb4c",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/fish.xn--q9jyb4c",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -60,6 +55,12 @@
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/entity/1",
|
||||
"type": "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/entity/1",
|
||||
"type": "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/entity/1",
|
||||
"type": "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds":
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"publicIds" :
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
},
|
||||
{
|
||||
"rel": "about",
|
||||
"href": "http://my.fake.url",
|
||||
"type": "text/html",
|
||||
"value": "https://rdap.example.com/withSlash/"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"key" : "value",
|
||||
"rdapConformance" : [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices" :
|
||||
[
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"key": "value",
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
"icann_rdap_response_profile_1",
|
||||
"icann_rdap_technical_implementation_guide_1"
|
||||
],
|
||||
"notices":
|
||||
[
|
||||
@@ -36,12 +36,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":
|
||||
[
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and Registrars version 1.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Status Codes",
|
||||
"description":
|
||||
|
||||
+11
-13
@@ -48,23 +48,22 @@ much internally for this reason.
|
||||
|
||||
## Do you support RDAP?
|
||||
|
||||
We are working on an implementation of the Registry Data Access Protocol (RDAP),
|
||||
ICANN's proposed successor to WHOIS, which provides similar data to WHOIS, but
|
||||
in a structured format. The standard is defined in RFCs 7480 through 7484:
|
||||
We provide an implementation of the Registry Data Access Protocol (RDAP) which provides
|
||||
similar data to the outdated WHOIS protocol, but in a structured format. The
|
||||
standard is defined in STD 95 and its RFCs:
|
||||
|
||||
* [RFC 7480: HTTP Usage in the Registration Data Access Protocol
|
||||
(RDAP)](https://tools.ietf.org/html/rfc7480)
|
||||
* [RFC 7481: Security Services for the Registration Data Access Protocol
|
||||
(RDAP)](https://tools.ietf.org/html/rfc7481)
|
||||
* [RFC 7482: Registration Data Access Protocol (RDAP) Query
|
||||
Format](https://tools.ietf.org/html/rfc7482)
|
||||
* [RFC 7483: JSON Responses for the Registration Data Access Protocol
|
||||
(RDAP)](https://tools.ietf.org/html/rfc7483)
|
||||
* [RFC 7484: Finding the Authoritative Registration Data (RDAP)
|
||||
Service](https://tools.ietf.org/html/rfc7484)
|
||||
* [RFC 9082: Registration Data Access Protocol (RDAP) Query
|
||||
Format](https://tools.ietf.org/html/rfc9082)
|
||||
* [RFC 9083: JSON Responses for the Registration Data Access Protocol
|
||||
(RDAP)](https://tools.ietf.org/html/rfc9083)
|
||||
* [RFC 9224: Finding the Authoritative Registration Data (RDAP)
|
||||
Service](https://tools.ietf.org/html/rfc9224)
|
||||
|
||||
Some gaps in the implementation remain, but most of the functionality is
|
||||
currently available. If you access this endpoint on a running Nomulus system:
|
||||
If you access this endpoint on a running Nomulus system:
|
||||
|
||||
`https://{PROJECT-ID}.appspot.com/rdap/domains?name=ex*`
|
||||
|
||||
@@ -97,5 +96,4 @@ can specify a TLD after the domain name wildcard (e.g. abc*.tld), and you can
|
||||
specify .domain.tld after the nameserver wildcard (e.g. ns*.domain.tld). But you
|
||||
can't do anything else, like searching for nameservers with ns*.tld. When using
|
||||
a wildcard, we currently require a prefix of at least two characters, to avoid
|
||||
having someone search for *. There are other limitations to the system which we
|
||||
plan to address in the future.
|
||||
having someone search for *.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user