1
0
mirror of https://github.com/google/nomulus synced 2026-04-25 10:40:49 +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:
Ben McIlwain
2024-06-20 11:22:38 -04:00
committed by GitHub
parent 19819444af
commit c9437d8c72
32 changed files with 377 additions and 116 deletions

View File

@@ -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());

View File

@@ -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());
}
}

View File

@@ -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()) {

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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() {

View File

@@ -63,6 +63,7 @@ public abstract class DomainInfoData implements ResponseData {
abstract ImmutableSet<StatusValue> getStatusValues();
@XmlElement(name = "registrant")
@Nullable
abstract String getRegistrant();
@XmlElement(name = "contact")

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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",

View File

@@ -254,5 +254,4 @@ public abstract class ConsoleApiAction implements Runnable {
super(message);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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())

View File

@@ -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;

View File

@@ -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")))

View File

@@ -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());

View File

@@ -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");

View File

@@ -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");
}

View File

@@ -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");

View File

@@ -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();

View File

@@ -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();

View File

@@ -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");

View File

@@ -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"));
}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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()),

View File

@@ -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<>();

View File

@@ -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());

View File

@@ -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>

View File

@@ -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.