Add abuse entity to registrar entities

From to the RDAP response profile:

2.4.5. Abuse Contact (email, phone) - an RDAP server MUST include an *entity*
with the *abuse* role within the registrar *entity* which MUST include *tel*
and *email*, and MAY include other members

Even though this is a MUST - this field will only be shown if the registrar has a *visible* abuse contact.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=242684303
This commit is contained in:
guyben 2019-04-09 09:46:21 -07:00 committed by jianglai
parent bf0d83585a
commit d1f833b9bc
3 changed files with 109 additions and 63 deletions

View file

@ -85,12 +85,15 @@ public class RdapJsonFormatter {
@Inject RdapJsonFormatter() {}
/**
* What type of data to generate. Summary data includes only information about the object itself,
* while full data includes associated items (e.g. for domains, full data includes the hosts,
* contacts and history entries connected with the domain). Summary data is appropriate for search
* queries which return many results, to avoid load on the system. According to the ICANN
* operational profile, a remark must be attached to the returned object indicating that it
* includes only summary data.
* What type of data to generate.
*
* <p>Summary data includes only information about the object itself, while full data includes
* associated items (e.g. for domains, full data includes the hosts, contacts and history entries
* connected with the domain).
*
* <p>Summary data is appropriate for search queries which return many results, to avoid load on
* the system. According to the ICANN operational profile, a remark must be attached to the
* returned object indicating that it includes only summary data.
*/
public enum OutputDataType {
FULL,
@ -170,8 +173,7 @@ public class RdapJsonFormatter {
}
/** Map of EPP status values to the RDAP equivalents. */
private static final ImmutableMap<StatusValue, RdapStatus> statusToRdapStatusMap =
Maps.immutableEnumMap(
private static final ImmutableMap<StatusValue, RdapStatus> STATUS_TO_RDAP_STATUS_MAP =
new ImmutableMap.Builder<StatusValue, RdapStatus>()
// RdapStatus.ADD_PERIOD not defined in our system
// RdapStatus.AUTO_RENEW_PERIOD not defined in our system
@ -197,7 +199,7 @@ public class RdapJsonFormatter {
.put(StatusValue.SERVER_TRANSFER_PROHIBITED, RdapStatus.SERVER_TRANSFER_PROHIBITED)
.put(StatusValue.SERVER_UPDATE_PROHIBITED, RdapStatus.SERVER_UPDATE_PROHIBITED)
// RdapStatus.TRANSFER_PERIOD not defined in our system
.build());
.build();
/** Role values specified in RFC 7483 § 10.2.4. */
private enum RdapEntityRole {
@ -248,8 +250,7 @@ public class RdapJsonFormatter {
/** Map of EPP event values to the RDAP equivalents. */
private static final ImmutableMap<HistoryEntry.Type, RdapEventAction>
historyEntryTypeToRdapEventActionMap =
Maps.immutableEnumMap(
HISTORY_ENTRY_TYPE_TO_RDAP_EVENT_ACTION_MAP =
new ImmutableMap.Builder<HistoryEntry.Type, RdapEventAction>()
.put(HistoryEntry.Type.CONTACT_CREATE, RdapEventAction.REGISTRATION)
.put(HistoryEntry.Type.CONTACT_DELETE, RdapEventAction.DELETION)
@ -262,7 +263,7 @@ public class RdapJsonFormatter {
.put(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE, RdapEventAction.TRANSFER)
.put(HistoryEntry.Type.HOST_CREATE, RdapEventAction.REGISTRATION)
.put(HistoryEntry.Type.HOST_DELETE, RdapEventAction.DELETION)
.build());
.build();
private static final ImmutableList<String> CONFORMANCE_LIST =
ImmutableList.of(RDAP_CONFORMANCE_LEVEL);
@ -598,6 +599,8 @@ public class RdapJsonFormatter {
}
ImmutableList.Builder<ImmutableMap<String, Object>> builder = new ImmutableList.Builder<>();
builder.addAll(entities);
// TODO(b/130150723): we need to display the ABUSE contact for registrar object inside of Domain
// responses. Currently, we use summary for any "internal" registrar.
builder.add(
makeRdapJsonForRegistrar(
registrar.get(),
@ -901,12 +904,12 @@ public class RdapJsonFormatter {
}
// include the registrar contacts as subentities
ImmutableList<ImmutableMap<String, Object>> registrarContacts =
registrar
.getContacts()
.stream()
.filter(RdapJsonFormatter::isVisible)
registrar.getContacts().stream()
.map(registrarContact -> makeRdapJsonForRegistrarContact(registrarContact, null))
.filter(entity -> !entity.isEmpty())
.collect(toImmutableList());
// TODO(b/117242274): add a warning (severe?) log if registrar has no ABUSE contact, as having
// one is required by the RDAP response profile
if (!registrarContacts.isEmpty()) {
jsonBuilder.put("entities", registrarContacts);
}
@ -930,12 +933,18 @@ public class RdapJsonFormatter {
/**
* Creates a JSON object for a {@link RegistrarContact}.
*
* <p>Returns an empty object if this contact shouldn't be visible (doesn't have a role).
*
* @param registrarContact the registrar contact for which the JSON object should be created
* @param whoisServer the fully-qualified domain name of the WHOIS server to be listed in the
* port43 field; if null, port43 is not added to the object
*/
static ImmutableMap<String, Object> makeRdapJsonForRegistrarContact(
RegistrarContact registrarContact, @Nullable String whoisServer) {
ImmutableList<String> roles = makeRdapRoleList(registrarContact);
if (roles.isEmpty()) {
return ImmutableMap.of();
}
ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>();
jsonBuilder.put("objectClassName", "entity");
String gaeUserId = registrarContact.getGaeUserId();
@ -943,7 +952,7 @@ public class RdapJsonFormatter {
jsonBuilder.put("handle", registrarContact.getGaeUserId());
}
jsonBuilder.put("status", STATUS_LIST_ACTIVE);
jsonBuilder.put("roles", makeRdapRoleList(registrarContact));
jsonBuilder.put("roles", roles);
// Create the vCard.
ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>();
vcardBuilder.add(VCARD_ENTRY_VERSION);
@ -988,6 +997,14 @@ public class RdapJsonFormatter {
/**
* Creates the list of RDAP roles for a registrar contact, using the visibleInWhoisAs* flags.
*
* <p>Only contacts with a non-empty role list should be visible.
*
* <p>The RDAP response profile only mandates the "abuse" entity:
*
* <p>2.4.5. Abuse Contact (email, phone) - an RDAP server MUST include an *entity* with the
* *abuse* role within the registrar *entity* which MUST include *tel* and *email*, and MAY
* include other members
*/
private static ImmutableList<String> makeRdapRoleList(RegistrarContact registrarContact) {
ImmutableList.Builder<String> rolesBuilder = new ImmutableList.Builder<>();
@ -997,13 +1014,10 @@ public class RdapJsonFormatter {
if (registrarContact.getVisibleInWhoisAsTech()) {
rolesBuilder.add(RdapEntityRole.TECH.rfc7483String);
}
return rolesBuilder.build();
if (registrarContact.getVisibleInDomainWhoisAsAbuse()) {
rolesBuilder.add(RdapEntityRole.ABUSE.rfc7483String);
}
/** Checks whether the registrar contact should be visible (because it has visible roles). */
private static boolean isVisible(RegistrarContact registrarContact) {
return registrarContact.getVisibleInWhoisAsAdmin()
|| registrarContact.getVisibleInWhoisAsTech();
return rolesBuilder.build();
}
/**
@ -1025,7 +1039,7 @@ public class RdapJsonFormatter {
for (HistoryEntry historyEntry :
ofy().load().type(HistoryEntry.class).ancestor(resource).order("modificationTime")) {
RdapEventAction rdapEventAction =
historyEntryTypeToRdapEventActionMap.get(historyEntry.getType());
HISTORY_ENTRY_TYPE_TO_RDAP_EVENT_ACTION_MAP.get(historyEntry.getType());
// Only save the historyEntries if this is a type we care about.
if (rdapEventAction == null) {
continue;
@ -1211,7 +1225,7 @@ public class RdapJsonFormatter {
Stream<RdapStatus> stream =
statusValues
.stream()
.map(status -> statusToRdapStatusMap.getOrDefault(status, RdapStatus.OBSCURED));
.map(status -> STATUS_TO_RDAP_STATUS_MAP.getOrDefault(status, RdapStatus.OBSCURED));
if (isRedacted) {
stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED));
}

View file

@ -54,6 +54,22 @@
],
"entities" :
[
{
"objectClassName" : "entity",
"status" : ["active"],
"roles" : ["abuse"],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "Jake Doe"],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2125551216"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2125551216"],
["email", {}, "text", "jakedoe@example.com"]
]
],
},
{
"objectClassName" : "entity",
"status" : ["active"],

View file

@ -38,6 +38,22 @@
],
"entities" :
[
{
"objectClassName" : "entity",
"status" : ["active"],
"roles" : ["abuse"],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "Jake Doe"],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2125551216"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2125551216"],
["email", {}, "text", "jakedoe@example.com"]
]
],
},
{
"objectClassName" : "entity",
"status" : ["active"],