Redact WHOIS output instead of removing the fields altogether

See https://www.icann.org/resources/pages/gtld-registration-data-specs-en/#appendixA for details on how certain fields are redacted.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199295355
This commit is contained in:
jianglai 2018-06-05 07:45:00 -07:00 committed by Ben McIlwain
parent e1bcc2e64d
commit 643b30d31f
6 changed files with 236 additions and 70 deletions

View file

@ -81,7 +81,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
.stream()
.filter(RegistrarContact::getVisibleInDomainWhoisAsAbuse)
.findFirst();
DomainEmitter domainEmitter =
return WhoisResponseResults.create(
new DomainEmitter()
.emitField(
"Domain Name",
@ -104,25 +104,22 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
"Registrar Abuse Contact Phone",
abuseContact.map(RegistrarContact::getPhoneNumber).orElse(null))
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
.emitContact(
"Registrant", Optional.of(domain.getRegistrant()), preferUnicode, fullOutput);
if (fullOutput) {
domainEmitter
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode, fullOutput)
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode, fullOutput)
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode, fullOutput);
}
domainEmitter
.emitSet(
"Name Server",
domain.loadNameserverFullyQualifiedHostNames(),
hostName -> maybeFormatHostname(hostName, preferUnicode))
.emitField("DNSSEC", isNullOrEmpty(domain.getDsData()) ? "unsigned" : "signedDelegation")
.emitWicfLink()
.emitLastUpdated(getTimestamp())
.emitAwipMessage()
.emitFooter(disclaimer);
return WhoisResponseResults.create(domainEmitter.toString(), 1);
.emitContact("Registrant", Optional.of(domain.getRegistrant()), preferUnicode)
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode)
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode)
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode)
.emitSet(
"Name Server",
domain.loadNameserverFullyQualifiedHostNames(),
hostName -> maybeFormatHostname(hostName, preferUnicode))
.emitField(
"DNSSEC", isNullOrEmpty(domain.getDsData()) ? "unsigned" : "signedDelegation")
.emitWicfLink()
.emitLastUpdated(getTimestamp())
.emitAwipMessage()
.emitFooter(disclaimer)
.toString(),
1);
}
/** Returns the contact of the given type. */
@ -139,17 +136,15 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
if (phoneNumber == null) {
return this;
}
return emitFieldIfDefined(ImmutableList.of(contactType, title), phoneNumber.getPhoneNumber())
return emitFieldIfDefined(
ImmutableList.of(contactType, title), phoneNumber.getPhoneNumber(), fullOutput)
.emitFieldIfDefined(
ImmutableList.of(contactType, title, "Ext"), phoneNumber.getExtension());
ImmutableList.of(contactType, title, "Ext"), phoneNumber.getExtension(), fullOutput);
}
/** Emit the contact entry of the given type. */
DomainEmitter emitContact(
String contactType,
Optional<Key<ContactResource>> contact,
boolean preferUnicode,
boolean fullOutput) {
String contactType, Optional<Key<ContactResource>> contact, boolean preferUnicode) {
if (!contact.isPresent()) {
return this;
}
@ -168,24 +163,21 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
preferUnicode,
contactResource.getLocalizedPostalInfo(),
contactResource.getInternationalizedPostalInfo());
if (fullOutput) {
// If the full output is to be displayed, show all fields for all contact types.
// ICANN Consistent Labeling & Display policy requires that this be the ROID.
emitField(ImmutableList.of("Registry", contactType, "ID"), contactResource.getRepoId());
if (postalInfo != null) {
emitFieldIfDefined(ImmutableList.of(contactType, "Name"), postalInfo.getName());
emitFieldIfDefined(ImmutableList.of(contactType, "Organization"), postalInfo.getOrg());
emitAddress(contactType, postalInfo.getAddress(), fullOutput);
}
emitPhone(contactType, "Phone", contactResource.getVoiceNumber());
emitPhone(contactType, "Fax", contactResource.getFaxNumber());
emitField(ImmutableList.of(contactType, "Email"), contactResource.getEmailAddress());
} else {
if (postalInfo != null) {
emitFieldIfDefined(ImmutableList.of(contactType, "Organization"), postalInfo.getOrg());
emitAddress(contactType, postalInfo.getAddress(), fullOutput);
}
// ICANN Consistent Labeling & Display policy requires that this be the ROID.
emitField(
ImmutableList.of("Registry", contactType, "ID"), contactResource.getRepoId(), fullOutput);
if (postalInfo != null) {
emitFieldIfDefined(ImmutableList.of(contactType, "Name"), postalInfo.getName(), fullOutput);
emitFieldIfDefined(
ImmutableList.of(contactType, "Organization"),
postalInfo.getOrg(),
fullOutput || contactType.equals("Registrant"));
emitAddress(contactType, postalInfo.getAddress(), fullOutput);
}
emitPhone(contactType, "Phone", contactResource.getVoiceNumber());
emitPhone(contactType, "Fax", contactResource.getFaxNumber());
emitField(
ImmutableList.of(contactType, "Email"), contactResource.getEmailAddress(), fullOutput);
return this;
}

View file

@ -39,6 +39,9 @@ abstract class WhoisResponseImpl implements WhoisResponse {
/** ICANN problem reporting URL appended to all WHOIS responses. */
private static final String ICANN_REPORTING_URL = "https://www.icann.org/wicf/";
/** Text to display when the field is redacted for privacy. */
static final String REDACT_TEXT = "REDACTED FOR PRIVACY";
/** The time at which this response was created. */
private final DateTime timestamp;
@ -94,59 +97,105 @@ abstract class WhoisResponseImpl implements WhoisResponse {
return emitList(title, values.stream().map(transform).sorted().collect(toImmutableList()));
}
/** Helper method that loops over a list of values and calls {@link #emitField}. */
E emitList(String title, Iterable<String> values) {
/**
* Helper method that loops over a list of values and calls {@link #emitField}.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitList(String title, Iterable<String> values, boolean fullOutput) {
for (String value : values) {
emitField(title, value);
emitField(title, value, fullOutput);
}
return thisCastToDerived();
}
/** Emit the field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(String name, @Nullable String value) {
/** Helper method that loops over a list of values and calls {@link #emitField}. */
E emitList(String title, Iterable<String> values) {
return emitList(title, values, true);
}
/**
* Emit the field name and value followed by a newline, but only if a value exists.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitFieldIfDefined(String name, @Nullable String value, boolean fullOutput) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
stringBuilder.append(cleanse(name)).append(':');
stringBuilder.append(' ').append(cleanse(value));
stringBuilder.append(' ').append(fullOutput ? cleanse(value) : REDACT_TEXT);
return emitNewline();
}
/** Emit the field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(String name, @Nullable String value) {
return emitFieldIfDefined(name, value, true);
}
/**
* Emit a multi-part field name and value followed by a newline, but only if a value exists.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitFieldIfDefined(List<String> nameParts, String value, boolean fullOutput) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
return emitField(nameParts, value, fullOutput);
}
/** Emit a multi-part field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(List<String> nameParts, String value) {
return emitFieldIfDefined(nameParts, value, true);
}
/**
* Emit the field name and value followed by a newline. /*
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitField(String name, @Nullable String value, boolean fullOutput) {
stringBuilder.append(cleanse(name)).append(':');
if (!isNullOrEmpty(value)) {
stringBuilder.append(' ').append(fullOutput ? cleanse(value) : REDACT_TEXT);
}
return emitNewline();
}
/** Emit the field name and value followed by a newline. */
E emitField(String name, @Nullable String value) {
stringBuilder.append(cleanse(name)).append(':');
if (!isNullOrEmpty(value)) {
stringBuilder.append(' ').append(cleanse(value));
}
return emitNewline();
return emitField(name, value, true);
}
/** Emit a multi-part field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(List<String> nameParts, String value) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
return emitField(nameParts, value);
/**
* Emit a multi-part field name and value followed by a newline.
*
* <p>This method redacts the output unless {@code fullOutput} is {@code true}.
*/
E emitField(List<String> nameParts, String value, boolean fullOutput) {
return emitField(Joiner.on(' ').join(nameParts), value, fullOutput);
}
/** Emit a multi-part field name and value followed by a newline. */
E emitField(List<String> nameParts, String value) {
return emitField(Joiner.on(' ').join(nameParts), value);
return emitField(nameParts, value, true);
}
/** Emit a contact address. */
E emitAddress(@Nullable String prefix, @Nullable Address address, boolean fullOutput) {
prefix = isNullOrEmpty(prefix) ? "" : prefix + " ";
if (address != null) {
if (fullOutput) {
emitList(prefix + "Street", address.getStreet());
emitField(prefix + "City", address.getCity());
}
emitField(prefix + "State/Province", address.getState());
if (fullOutput) {
emitField(prefix + "Postal Code", address.getZip());
}
emitField(prefix + "Country", address.getCountryCode());
emitList(prefix + "Street", address.getStreet(), fullOutput);
emitField(prefix + "City", address.getCity(), fullOutput);
emitField(
prefix + "State/Province",
address.getState(),
(fullOutput || prefix.equals("Registrant ")));
emitField(prefix + "Postal Code", address.getZip(), fullOutput);
emitField(
prefix + "Country",
address.getCountryCode(),
fullOutput || prefix.equals("Registrant "));
}
return thisCastToDerived();
}

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.lol
Name Server: ns2.cat.lol
DNSSEC: signedDelegation

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.xn--q9jyb4c
Name Server: ns2.cat.xn--q9jyb4c
DNSSEC: signedDelegation

View file

@ -13,9 +13,39 @@ Domain Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibit
Domain Status: clientRenewProhibited https://icann.org/epp#clientRenewProhibited
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: GOOGLE INCORPORATED <script>
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: BM
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: US
Registrant Phone: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Admin Email: REDACTED FOR PRIVACY
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 Fax: REDACTED FOR PRIVACY
Tech Email: REDACTED FOR PRIVACY
Name Server: ns1.cat.みんな
Name Server: ns2.cat.みんな
DNSSEC: signedDelegation

View file

@ -15,9 +15,44 @@ 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 Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY
Registrant Organization: Tom & Jerry Corp.
Registrant Street: REDACTED FOR PRIVACY
Registrant City: REDACTED FOR PRIVACY
Registrant State/Province: AP
Registrant Postal Code: REDACTED FOR PRIVACY
Registrant Country: EX
Registrant Phone: REDACTED FOR PRIVACY
Registrant Phone Ext: REDACTED FOR PRIVACY
Registrant Fax: REDACTED FOR PRIVACY
Registrant Fax Ext: REDACTED FOR PRIVACY
Registrant Email: REDACTED FOR PRIVACY
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: REDACTED FOR PRIVACY
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: REDACTED FOR PRIVACY
Name Server: ns01.exampleregistrar.tld
Name Server: ns02.exampleregistrar.tld
DNSSEC: signedDelegation