mirror of
https://github.com/google/nomulus
synced 2026-04-24 10:10:46 +00:00
Make registrant nullable on domains (#2477)
This is the first step in migrating to the minimum registration data set. Note that our database model already permits null domain registrants, so this just makes the code accept it as well. Note that I haven't changed any requirements in EPP flows yet; a later step will be to check the migration schedule and then not require the registrant to be present if in a suitable state. This does potentially affect the output of WHOIS/RDAP, but that's a NOOP so long as EPP commands and other tools continue to enforce the requirement of a registrant.
This commit is contained in:
@@ -468,7 +468,7 @@ public class RdePipeline implements Serializable {
|
||||
HashSet<Serializable> contacts = new HashSet<>();
|
||||
contacts.add(domain.getAdminContact().getKey());
|
||||
contacts.add(domain.getTechContact().getKey());
|
||||
contacts.add(domain.getRegistrant().getKey());
|
||||
domain.getRegistrant().ifPresent(r -> contacts.add(r.getKey()));
|
||||
// Billing contact is not mandatory.
|
||||
if (domain.getBillingContact() != null) {
|
||||
contacts.add(domain.getBillingContact().getKey());
|
||||
|
||||
@@ -412,11 +412,13 @@ public class DomainFlowUtils {
|
||||
|
||||
/** Verify that no linked resources have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts, VKey<Contact> registrant, Set<VKey<Host>> nameservers)
|
||||
Set<DesignatedContact> contacts,
|
||||
Optional<VKey<Contact>> registrant,
|
||||
Set<VKey<Host>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
|
||||
registrant.ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
}
|
||||
@@ -480,9 +482,10 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresent(
|
||||
@Nullable VKey<Contact> registrant, Set<DesignatedContact> contacts)
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
if (registrant == null) {
|
||||
// TODO: Check minimum reg data set migration schedule here and don't throw when any are empty.
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
|
||||
@@ -498,14 +501,14 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
|
||||
static void validateRegistrantAllowedOnTld(String tld, Optional<String> registrantContactId)
|
||||
throws RegistrantNotAllowedException {
|
||||
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
|
||||
// Empty allow list or null registrantContactId are ignored.
|
||||
if (registrantContactId != null
|
||||
if (registrantContactId.isPresent()
|
||||
&& !allowedRegistrants.isEmpty()
|
||||
&& !allowedRegistrants.contains(registrantContactId)) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId);
|
||||
&& !allowedRegistrants.contains(registrantContactId.get())) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,8 +119,11 @@ public final class DomainInfoFlow implements TransactionalFlow {
|
||||
.setCreationTime(domain.getCreationTime())
|
||||
.setLastEppUpdateTime(domain.getLastEppUpdateTime())
|
||||
.setRegistrationExpirationTime(domain.getRegistrationExpirationTime())
|
||||
.setLastTransferTime(domain.getLastTransferTime())
|
||||
.setRegistrant(tm().loadByKey(domain.getRegistrant()).getContactId());
|
||||
.setLastTransferTime(domain.getLastTransferTime());
|
||||
domain
|
||||
.getRegistrant()
|
||||
.ifPresent(r -> infoBuilder.setRegistrant(tm().loadByKey(r).getContactId()));
|
||||
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid.
|
||||
if (registrarId.equals(domain.getCurrentSponsorRegistrarId()) || authInfo.isPresent()) {
|
||||
|
||||
@@ -278,7 +278,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setRegistrant(change.getRegistrant().or(domain::getRegistrant))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
@@ -301,7 +301,10 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
}
|
||||
|
||||
private static void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
if (change.getRegistrantContactId() != null && change.getRegistrantContactId().isEmpty()) {
|
||||
// TODO(mcilwain): Make this check the minimum registration data set migration schedule
|
||||
// and not require presence of a registrant in later stages.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public class DomainBase extends EppResource
|
||||
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
@Expose @Nullable VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@@ -585,8 +585,8 @@ public class DomainBase extends EppResource
|
||||
}
|
||||
|
||||
/** A key to the registrant who registered this domain. */
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrantContact;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrantContact);
|
||||
}
|
||||
|
||||
public VKey<Contact> getAdminContact() {
|
||||
@@ -606,6 +606,11 @@ public class DomainBase extends EppResource
|
||||
return getAllContacts(false);
|
||||
}
|
||||
|
||||
/** Gets all associated contacts for the domain, including the registrant. */
|
||||
public ImmutableSet<DesignatedContact> getAllContacts() {
|
||||
return getAllContacts(true);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
return authInfo;
|
||||
}
|
||||
@@ -717,7 +722,6 @@ public class DomainBase extends EppResource
|
||||
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
|
||||
|
||||
checkArgumentNotNull(emptyToNull(instance.domainName), "Missing domainName");
|
||||
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
|
||||
instance.tld = getTldFromDomainName(instance.domainName);
|
||||
|
||||
T newDomain = super.build();
|
||||
@@ -749,9 +753,9 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setRegistrant(VKey<Contact> registrant) {
|
||||
public B setRegistrant(Optional<VKey<Contact>> registrant) {
|
||||
// Set the registrant field specifically.
|
||||
getInstance().registrantContact = registrant;
|
||||
getInstance().registrantContact = registrant.orElse(null);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
@@ -76,21 +77,21 @@ public class DomainCommand {
|
||||
|
||||
/** The contactId of the registrant who registered this domain. */
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
String registrantContactId;
|
||||
|
||||
/** A resolved key to the registrant who registered this domain. */
|
||||
@XmlTransient VKey<Contact> registrant;
|
||||
@Nullable @XmlTransient VKey<Contact> registrant;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
DomainAuthInfo authInfo;
|
||||
|
||||
public String getRegistrantContactId() {
|
||||
return registrantContactId;
|
||||
public Optional<String> getRegistrantContactId() {
|
||||
return Optional.ofNullable(registrantContactId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrant;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrant);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
|
||||
@@ -63,6 +63,7 @@ public abstract class DomainInfoData implements ResponseData {
|
||||
abstract ImmutableSet<StatusValue> getStatusValues();
|
||||
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
abstract String getRegistrant();
|
||||
|
||||
@XmlElement(name = "contact")
|
||||
|
||||
@@ -76,7 +76,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// they are global, and might have different roles for different domains.
|
||||
if (contact.isPresent() && isAuthorized(contact.get())) {
|
||||
return rdapJsonFormatter.createRdapContactEntity(
|
||||
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
|
||||
contact, ImmutableSet.of(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,7 +461,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
.entitySearchResultsBuilder()
|
||||
.add(
|
||||
rdapJsonFormatter.createRdapContactEntity(
|
||||
contact, ImmutableSet.of(), outputDataType));
|
||||
Optional.of(contact), ImmutableSet.of(), outputDataType));
|
||||
newCursor =
|
||||
Optional.of(
|
||||
CONTACT_CURSOR_PREFIX
|
||||
|
||||
@@ -61,6 +61,7 @@ import google.registry.rdap.RdapDataStructures.RdapStatus;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapContactEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapDomain;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity.Role;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.SecureDns;
|
||||
@@ -74,6 +75,7 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -249,6 +251,16 @@ public class RdapJsonFormatter {
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
Ordering.natural().onResultOf(DesignatedContact::getType);
|
||||
|
||||
/**
|
||||
* The list of RDAP contact roles that are required to be present on each domain.
|
||||
*
|
||||
* <p>Per RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and
|
||||
* MAY have others. We also have the BILLING role in our system but it isn't required and is only
|
||||
* listed if actually present.
|
||||
*/
|
||||
private static final ImmutableSet<Role> REQUIRED_CONTACT_ROLES =
|
||||
ImmutableSet.of(Role.REGISTRANT, Role.ADMIN, Role.TECH);
|
||||
|
||||
/** Creates the TOS notice that is added to every reply. */
|
||||
Notice createTosNotice() {
|
||||
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
|
||||
@@ -371,36 +383,48 @@ public class RdapJsonFormatter {
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> loadedContacts =
|
||||
replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(domain.getReferencedContacts()));
|
||||
// RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY
|
||||
// have others. We also add the BILLING.
|
||||
//
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses redaction of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// the GDPR redaction is handled in createRdapContactEntity
|
||||
// The GDPR redaction is handled in createRdapContactEntity.
|
||||
|
||||
// Load all contacts that are present and group them by type (it is common for a single contact
|
||||
// entity to be used across multiple contact types on domain, e.g. registrant and admin).
|
||||
ImmutableSetMultimap<VKey<Contact>, Type> contactsToRoles =
|
||||
Streams.concat(
|
||||
domain.getContacts().stream(),
|
||||
Stream.of(DesignatedContact.create(Type.REGISTRANT, domain.getRegistrant())))
|
||||
domain.getAllContacts().stream()
|
||||
.sorted(DESIGNATED_CONTACT_ORDERING)
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getContactKey, DesignatedContact::getType));
|
||||
|
||||
// Convert the contact entities to RDAP output contacts (this also converts the contact types
|
||||
// to RDAP roles).
|
||||
Set<RdapContactEntity> rdapContacts = new LinkedHashSet<>();
|
||||
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
|
||||
Set<RdapEntity.Role> roles =
|
||||
Set<Role> roles =
|
||||
contactsToRoles.get(contactKey).stream()
|
||||
.map(RdapJsonFormatter::convertContactTypeToRdapRole)
|
||||
.collect(toImmutableSet());
|
||||
if (roles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
builder
|
||||
.entitiesBuilder()
|
||||
.add(
|
||||
createRdapContactEntity(
|
||||
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.ofNullable(loadedContacts.get(contactKey)), roles, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// Loop through all required contact roles and fill in placeholder REDACTED info for any
|
||||
// required ones that are missing, i.e. because of minimum registration data set.
|
||||
for (Role role : REQUIRED_CONTACT_ROLES) {
|
||||
if (rdapContacts.stream().noneMatch(c -> c.roles().contains(role))) {
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.empty(), ImmutableSet.of(role), OutputDataType.INTERNAL));
|
||||
}
|
||||
}
|
||||
builder.entitiesBuilder().addAll(rdapContacts);
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
@@ -502,25 +526,35 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a JSON object for a {@link Contact} and associated contact type.
|
||||
*
|
||||
* <p>If the contact isn't present (i.e. because of minimum registration data set), then always
|
||||
* show all of its fields as if they were redacted, and always deny RDAP authorization.
|
||||
*
|
||||
* @param contact the contact resource object from which the JSON object should be created
|
||||
* @param roles the roles of this contact
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapContactEntity createRdapContactEntity(
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
Optional<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.
|
||||
// TODO(mcilwain): Once the RDAP profile is fully updated for minimum registration data set,
|
||||
// we will want to not include non-existent contacts at all, rather than
|
||||
// pretending they exist and just showing REDACTED info. This is especially
|
||||
// important for authorized flows, where you wouldn't expect to see redaction.
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
contact.isPresent()
|
||||
&& rdapAuthorization.isAuthorizedForRegistrar(
|
||||
contact.get().getCurrentSponsorRegistrarId());
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
fillRdapContactEntityWhenAuthorized(
|
||||
contactBuilder, vcardBuilder, contact.get(), outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -45,12 +44,11 @@ import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusValueType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsOrKeyType;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Utility class that turns {@link Domain} as {@link XjcRdeDomainElement}. */
|
||||
final class DomainToXjcConverter {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** Converts {@link Domain} to {@link XjcRdeDomainElement}. */
|
||||
static XjcRdeDomainElement convert(Domain domain, RdeMode mode) {
|
||||
return new XjcRdeDomainElement(convertDomain(domain, mode));
|
||||
@@ -168,11 +166,9 @@ final class DomainToXjcConverter {
|
||||
// o An OPTIONAL <registrant> element that contain the identifier for
|
||||
// the human or organizational social information object associated
|
||||
// as the holder of the domain name object.
|
||||
VKey<Contact> registrant = model.getRegistrant();
|
||||
if (registrant == null) {
|
||||
logger.atWarning().log("Domain %s has no registrant contact.", domainName);
|
||||
} else {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant));
|
||||
Optional<VKey<Contact>> registrant = model.getRegistrant();
|
||||
if (registrant.isPresent()) {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant.get()));
|
||||
checkState(
|
||||
registrantContact != null,
|
||||
"Registrant contact %s on domain %s does not exist",
|
||||
|
||||
@@ -254,5 +254,4 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
@Inject
|
||||
ConsoleUpdateRegistrarAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrar") Optional<Registrar> registrar) {
|
||||
ConsoleApiParams consoleApiParams, @Parameter("registrar") Optional<Registrar> registrar) {
|
||||
super(consoleApiParams);
|
||||
this.registrar = registrar;
|
||||
}
|
||||
@@ -108,5 +107,4 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -105,7 +105,9 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
"Registrar Abuse Contact Phone",
|
||||
abuseContact.map(RegistrarPoc::getPhoneNumber).orElse(""))
|
||||
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
|
||||
.emitContact("Registrant", Optional.of(domain.getRegistrant()), preferUnicode)
|
||||
// TODO(mcilwain): Investigate if the WHOIS spec requires us to always output REDACTED
|
||||
// text in WHOIS even if the contact is not present, and if so, do so.
|
||||
.emitContact("Registrant", domain.getRegistrant(), preferUnicode)
|
||||
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode)
|
||||
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode)
|
||||
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode)
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
@@ -191,7 +192,7 @@ public class RegistryJpaReadTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
|
||||
@@ -31,6 +31,7 @@ import static google.registry.rde.RdeResourceType.HOST;
|
||||
import static google.registry.rde.RdeResourceType.REGISTRAR;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
@@ -83,10 +84,10 @@ import google.registry.rde.PendingDeposit;
|
||||
import google.registry.rde.RdeResourceType;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeKeyringModule;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -266,23 +267,23 @@ public class RdePipelineTest {
|
||||
persistHostHistory(host1);
|
||||
Domain helloDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("hello.soy", contact1)
|
||||
.asBuilder()
|
||||
.addNameserver(host1.createVKey())
|
||||
.build());
|
||||
newDomain("hello.soy", contact1).asBuilder().addNameserver(host1.createVKey()).build());
|
||||
persistDomainHistory(helloDomain);
|
||||
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
|
||||
Host host2 = persistActiveHost("ns1.hello.soy");
|
||||
persistHostHistory(host2);
|
||||
|
||||
// This domain has no registrant.
|
||||
Domain kittyDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("kitty.fun", contact2)
|
||||
newDomain("kitty.fun", contact2)
|
||||
.asBuilder()
|
||||
.addNameservers(ImmutableSet.of(host1.createVKey(), host2.createVKey()))
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
persistDomainHistory(kittyDomain);
|
||||
// Should not appear because the TLD is not included in a pending deposit.
|
||||
persistDomainHistory(persistEppResource(DatabaseHelper.newDomain("lol.cat", contact1)));
|
||||
persistDomainHistory(persistEppResource(newDomain("lol.cat", contact1)));
|
||||
// To be deleted.
|
||||
Domain deletedDomain = persistActiveDomain("deleted.soy");
|
||||
persistDomainHistory(deletedDomain);
|
||||
@@ -325,7 +326,7 @@ public class RdePipelineTest {
|
||||
persistHostHistory(futureHost);
|
||||
persistDomainHistory(
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("future.soy", futureContact)
|
||||
newDomain("future.soy", futureContact)
|
||||
.asBuilder()
|
||||
.setNameservers(futureHost.createVKey())
|
||||
.build()));
|
||||
@@ -379,18 +380,30 @@ public class RdePipelineTest {
|
||||
// The same registrars are attached to all the pending deposits.
|
||||
.containsExactly("New Registrar", "The Registrar", "external_monitoring");
|
||||
// Domain fragments.
|
||||
if ("soy".equals(kv.getKey().tld())) {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("hello.soy");
|
||||
} else {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("cat.fun");
|
||||
ImmutableSet<DepositFragment> domainFrags =
|
||||
getFragmentForType(kv, DOMAIN).collect(toImmutableSet());
|
||||
assertThat(domainFrags).hasSize(1);
|
||||
if ("fun".equals(kv.getKey().tld())) {
|
||||
// Note that this fragment contains no registrant (which is valid).
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>cat.fun</rdeDomain:name>
|
||||
<rdeDomain:roid>15-FUN</rdeDomain:roid>
|
||||
<rdeDomain:uName>cat.fun</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:contact type="admin">contact456</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact456</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.hello.soy</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
if (kv.getKey().mode().equals(FULL)) {
|
||||
// Contact fragments for hello.soy.
|
||||
@@ -400,12 +413,35 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contact1234", "contact789");
|
||||
|
||||
// Host fragments for hello.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
.map(getXmlElement(HOST_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("ns1.external.tld", "ns1.lol.cat");
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains a registrant.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:registrant>contact1234</rdeDomain:registrant>
|
||||
<rdeDomain:contact type="admin">contact789</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact1234</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
} else {
|
||||
// Contact fragments for cat.fun.
|
||||
assertThat(
|
||||
@@ -413,6 +449,7 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contactABC");
|
||||
|
||||
// Host fragments for cat.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
@@ -429,6 +466,25 @@ public class RdePipelineTest {
|
||||
fragment.type().equals(CONTACT)
|
||||
|| fragment.type().equals(HOST)))
|
||||
.isFalse();
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains no contact info.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
|
||||
@@ -54,6 +54,7 @@ import google.registry.util.Retrier;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
@@ -297,7 +298,7 @@ class Spec11PipelineTest {
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateRegistrarId(registrar.getRegistrarId())
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
|
||||
@@ -103,6 +103,7 @@ import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
@@ -162,7 +163,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(TIME_BEFORE_FLOW)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.build());
|
||||
earlierHistoryEntry =
|
||||
@@ -738,7 +739,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey())
|
||||
Optional.of(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.setDeletionTime(START_OF_TIME)
|
||||
.build());
|
||||
|
||||
@@ -78,6 +78,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
@@ -139,7 +140,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
|
||||
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
|
||||
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
|
||||
.setRegistrant(registrant.createVKey())
|
||||
.setRegistrant(Optional.of(registrant.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
@@ -227,6 +228,13 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
doSuccessfulTest("domain_info_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noRegistrant() throws Exception {
|
||||
persistTestEntities(false);
|
||||
domain = persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
doSuccessfulTest("domain_info_response_no_registrant.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_clTridNotSpecified() throws Exception {
|
||||
setEppInput("domain_info_no_cltrid.xml");
|
||||
|
||||
@@ -162,7 +162,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
|
||||
.setRegistrant(mak21Contact.createVKey())
|
||||
.setRegistrant(Optional.of(mak21Contact.createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
persistResource(
|
||||
@@ -338,7 +338,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.asBuilder()
|
||||
.setNameservers(nameservers.build())
|
||||
.setContacts(ImmutableSet.copyOf(contacts.subList(0, 3)))
|
||||
.setRegistrant(contacts.get(3).getContactKey())
|
||||
.setRegistrant(Optional.of(contacts.get(3).getContactKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
assertMutatingFlow(true);
|
||||
@@ -348,7 +348,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(domain.getNameservers()).hasSize(13);
|
||||
// getContacts does not return contacts of type REGISTRANT, so check these separately.
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
assertThat(loadByKey(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
|
||||
assertThat(loadByKey(domain.getRegistrant().get()).getContactId()).isEqualTo("max_test_7");
|
||||
assertNoBillingEvents();
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
@@ -426,7 +426,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013.createVKey())
|
||||
.setRegistrant(Optional.of(sh8013.createVKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -441,7 +441,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013Key)
|
||||
.setRegistrant(Optional.of(sh8013Key))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Key),
|
||||
@@ -1590,7 +1590,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
|
||||
.build());
|
||||
runFlow();
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
@@ -1605,7 +1605,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("mak21"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("mak21");
|
||||
|
||||
runFlow();
|
||||
@@ -1615,7 +1615,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("sh8013"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationW
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -69,7 +70,7 @@ public class DomainSqlTest {
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
private Domain domain;
|
||||
private VKey<Contact> contactKey;
|
||||
private Optional<VKey<Contact>> contactKey;
|
||||
private VKey<Contact> contact2Key;
|
||||
private VKey<Host> host1VKey;
|
||||
private Host host;
|
||||
@@ -82,7 +83,7 @@ public class DomainSqlTest {
|
||||
saveRegistrar("registrar1");
|
||||
saveRegistrar("registrar2");
|
||||
saveRegistrar("registrar3");
|
||||
contactKey = createKey(Contact.class, "contact_id1");
|
||||
contactKey = Optional.of(createKey(Contact.class, "contact_id1"));
|
||||
contact2Key = createKey(Contact.class, "contact_id2");
|
||||
|
||||
host1VKey = createKey(Host.class, "host1");
|
||||
|
||||
@@ -185,7 +185,7 @@ public class DomainTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact1Key)
|
||||
.setRegistrant(Optional.of(contact1Key))
|
||||
.setNameservers(ImmutableSet.of(hostKey))
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
@@ -242,6 +242,16 @@ public class DomainTest {
|
||||
.hasValue(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrantNotRequired() {
|
||||
persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
assertThat(
|
||||
loadByForeignKey(Domain.class, domain.getForeignKey(), fakeClock.nowUtc())
|
||||
.get()
|
||||
.getRegistrant())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringsBecomeNull() {
|
||||
assertThat(
|
||||
@@ -1012,14 +1022,14 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
true);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
|
||||
// Make sure everything gets nulled out.
|
||||
domain.setContactFields(ImmutableSet.of(), true);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
@@ -1032,13 +1042,13 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
false);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
domain = domain.asBuilder().setRegistrant(contact1Key).build();
|
||||
domain = domain.asBuilder().setRegistrant(Optional.of(contact1Key)).build();
|
||||
domain.setContactFields(ImmutableSet.of(), false);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -68,7 +69,7 @@ public final class Spec11ThreatMatchTest extends EntityTestCase {
|
||||
.setDomainName("foo.tld")
|
||||
.setRepoId(domainRepoId)
|
||||
.setNameservers(hostVKey)
|
||||
.setRegistrant(registrantContactVKey)
|
||||
.setRegistrant(Optional.of(registrantContactVKey))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build();
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
@@ -278,6 +279,19 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_evenWhenRegistrantDoesntExist() {
|
||||
// Even though the registrant is empty on this domain, it still shows a full set of REDACTED
|
||||
// fields through RDAP.
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_loggedInAsOtherRegistrar_noContacts() {
|
||||
login("idnregistrar");
|
||||
|
||||
@@ -52,6 +52,8 @@ import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
|
||||
import google.registry.rdap.RdapObjectClasses.TopLevelReplyObject;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -77,7 +79,7 @@ class RdapJsonFormatterTest {
|
||||
private Host hostNoAddresses;
|
||||
private Host hostNotLinked;
|
||||
private Host hostSuperordinatePendingTransfer;
|
||||
private Contact contactRegistrant;
|
||||
@Nullable private Contact contactRegistrant;
|
||||
private Contact contactAdmin;
|
||||
private Contact contactTech;
|
||||
private Contact contactNotLinked;
|
||||
@@ -371,7 +373,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -384,7 +386,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.SUMMARY)
|
||||
.toJson())
|
||||
@@ -398,7 +400,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -418,7 +420,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -431,7 +433,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactAdmin, ImmutableSet.of(RdapEntity.Role.ADMIN), OutputDataType.FULL)
|
||||
Optional.of(contactAdmin),
|
||||
ImmutableSet.of(RdapEntity.Role.ADMIN),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_admincontact.json"));
|
||||
}
|
||||
@@ -442,7 +446,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactTech, ImmutableSet.of(RdapEntity.Role.TECH), OutputDataType.FULL)
|
||||
Optional.of(contactTech),
|
||||
ImmutableSet.of(RdapEntity.Role.TECH),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_techcontact.json"));
|
||||
}
|
||||
@@ -452,7 +458,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactTech, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactTech), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_rolelesscontact.json"));
|
||||
}
|
||||
@@ -462,7 +469,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactNotLinked, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactNotLinked), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_unlinkedcontact.json"));
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import google.registry.xjc.rdedomain.XjcRdeDomainElement;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -280,9 +281,14 @@ public class DomainToXjcConverterTest {
|
||||
makeHost(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef")
|
||||
.createVKey()))
|
||||
.setRegistrant(
|
||||
makeContact(
|
||||
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(
|
||||
clock,
|
||||
"12-Q9JYB4C",
|
||||
"5372808-ERL",
|
||||
"(◕‿◕) nevermore",
|
||||
"prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z"))
|
||||
.setGracePeriods(
|
||||
ImmutableSet.of(
|
||||
|
||||
@@ -51,6 +51,7 @@ import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -63,8 +64,9 @@ final class RdeFixtures {
|
||||
.setDomainName("example." + tld)
|
||||
.setRepoId(generateNewDomainRoid(tld))
|
||||
.setRegistrant(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.build();
|
||||
DomainHistory historyEntry =
|
||||
persistResource(
|
||||
|
||||
@@ -190,7 +190,7 @@ public final class DatabaseHelper {
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(START_OF_TIME)
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
|
||||
.setRegistrant(contactKey)
|
||||
.setRegistrant(Optional.of(contactKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contactKey),
|
||||
@@ -603,7 +603,7 @@ public final class DatabaseHelper {
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
|
||||
@@ -46,6 +46,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.util.Idn;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -351,7 +352,7 @@ public final class FullFieldsTestEntityHelper {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, "deadface")));
|
||||
if (registrant != null) {
|
||||
builder.setRegistrant(registrant.createVKey());
|
||||
builder.setRegistrant(Optional.of(registrant.createVKey()));
|
||||
}
|
||||
if ((admin != null) || (tech != null)) {
|
||||
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
@@ -41,6 +41,7 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.whois.WhoisResponse.WhoisResponseResults;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -264,7 +265,7 @@ class DomainWhoisResponseTest {
|
||||
StatusValue.CLIENT_RENEW_PROHIBITED,
|
||||
StatusValue.CLIENT_TRANSFER_PROHIBITED,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setRegistrant(registrantResourceKey)
|
||||
.setRegistrant(Optional.of(registrantResourceKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(DesignatedContact.Type.ADMIN, adminResourceKey),
|
||||
@@ -291,6 +292,21 @@ class DomainWhoisResponseTest {
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_noRegistrant() {
|
||||
DomainWhoisResponse domainWhoisResponse =
|
||||
new DomainWhoisResponse(
|
||||
domain.asBuilder().setRegistrant(Optional.empty()).build(),
|
||||
false,
|
||||
"Please contact registrar",
|
||||
clock.nowUtc());
|
||||
assertThat(
|
||||
domainWhoisResponse.getResponse(
|
||||
false,
|
||||
"Doodle Disclaimer\nI exist so that carriage return\nin disclaimer can be tested."))
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain_no_registrant.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_registrarAbuseInfoMissing() {
|
||||
persistResource(abuseContact.asBuilder().setVisibleInDomainWhoisAsAbuse(false).build());
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:infData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:roid>%ROID%</domain:roid>
|
||||
<domain:status s="ok"/>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:host>ns1.example.tld</domain:host>
|
||||
<domain:host>ns2.example.tld</domain:host>
|
||||
<domain:clID>NewRegistrar</domain:clID>
|
||||
<domain:crID>TheRegistrar</domain:crID>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:upID>NewRegistrar</domain:upID>
|
||||
<domain:upDate>1999-12-03T09:00:00.0Z</domain:upDate>
|
||||
<domain:exDate>2005-04-03T22:00:00.0Z</domain:exDate>
|
||||
<domain:trDate>2000-04-08T09:00:00.0Z</domain:trDate>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:infData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -0,0 +1,53 @@
|
||||
Domain Name: example.tld
|
||||
Registry Domain ID: 3-TLD
|
||||
Registrar WHOIS Server: whois.nic.fakewhois.example
|
||||
Registrar URL: http://my.fake.url
|
||||
Updated Date: 2009-05-29T20:13:00Z
|
||||
Creation Date: 2000-10-08T00:45:00Z
|
||||
Registry Expiry Date: 2010-10-08T00:44:59Z
|
||||
Registrar: New Registrar
|
||||
Registrar IANA ID: 5555555
|
||||
Registrar Abuse Contact Email: jakedoe@theregistrar.com
|
||||
Registrar Abuse Contact Phone: +1.2125551216
|
||||
Domain Status: addPeriod https://icann.org/epp#addPeriod
|
||||
Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited
|
||||
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
|
||||
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
|
||||
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
|
||||
Domain Status: transferPeriod https://icann.org/epp#transferPeriod
|
||||
Registry Admin ID: REDACTED FOR PRIVACY
|
||||
Admin Name: REDACTED FOR PRIVACY
|
||||
Admin Organization: REDACTED FOR PRIVACY
|
||||
Admin Street: REDACTED FOR PRIVACY
|
||||
Admin City: REDACTED FOR PRIVACY
|
||||
Admin State/Province: REDACTED FOR PRIVACY
|
||||
Admin Postal Code: REDACTED FOR PRIVACY
|
||||
Admin Country: REDACTED FOR PRIVACY
|
||||
Admin Phone: REDACTED FOR PRIVACY
|
||||
Admin Phone Ext: REDACTED FOR PRIVACY
|
||||
Admin Fax: REDACTED FOR PRIVACY
|
||||
Admin Email: Please contact registrar
|
||||
Registry Tech ID: REDACTED FOR PRIVACY
|
||||
Tech Name: REDACTED FOR PRIVACY
|
||||
Tech Organization: REDACTED FOR PRIVACY
|
||||
Tech Street: REDACTED FOR PRIVACY
|
||||
Tech City: REDACTED FOR PRIVACY
|
||||
Tech State/Province: REDACTED FOR PRIVACY
|
||||
Tech Postal Code: REDACTED FOR PRIVACY
|
||||
Tech Country: REDACTED FOR PRIVACY
|
||||
Tech Phone: REDACTED FOR PRIVACY
|
||||
Tech Phone Ext: REDACTED FOR PRIVACY
|
||||
Tech Fax: REDACTED FOR PRIVACY
|
||||
Tech Fax Ext: REDACTED FOR PRIVACY
|
||||
Tech Email: Please contact registrar
|
||||
Name Server: ns01.exampleregistrar.tld
|
||||
Name Server: ns02.exampleregistrar.tld
|
||||
DNSSEC: signedDelegation
|
||||
URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
|
||||
>>> Last update of WHOIS database: 2009-05-29T20:15:00Z <<<
|
||||
|
||||
For more information on Whois status codes, please visit https://icann.org/epp
|
||||
|
||||
Doodle Disclaimer
|
||||
I exist so that carriage return
|
||||
in disclaimer can be tested.
|
||||
Reference in New Issue
Block a user