mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 08:27:14 +02:00
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:
parent
15faefef25
commit
896cc252e7
3 changed files with 109 additions and 63 deletions
|
@ -85,12 +85,15 @@ public class RdapJsonFormatter {
|
||||||
@Inject RdapJsonFormatter() {}
|
@Inject RdapJsonFormatter() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What type of data to generate. Summary data includes only information about the object itself,
|
* What type of data to generate.
|
||||||
* 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
|
* <p>Summary data includes only information about the object itself, while full data includes
|
||||||
* queries which return many results, to avoid load on the system. According to the ICANN
|
* associated items (e.g. for domains, full data includes the hosts, contacts and history entries
|
||||||
* operational profile, a remark must be attached to the returned object indicating that it
|
* connected with the domain).
|
||||||
* includes only summary data.
|
*
|
||||||
|
* <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 {
|
public enum OutputDataType {
|
||||||
FULL,
|
FULL,
|
||||||
|
@ -170,34 +173,33 @@ public class RdapJsonFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Map of EPP status values to the RDAP equivalents. */
|
/** Map of EPP status values to the RDAP equivalents. */
|
||||||
private static final ImmutableMap<StatusValue, RdapStatus> statusToRdapStatusMap =
|
private static final ImmutableMap<StatusValue, RdapStatus> STATUS_TO_RDAP_STATUS_MAP =
|
||||||
Maps.immutableEnumMap(
|
new ImmutableMap.Builder<StatusValue, RdapStatus>()
|
||||||
new ImmutableMap.Builder<StatusValue, RdapStatus>()
|
// RdapStatus.ADD_PERIOD not defined in our system
|
||||||
// RdapStatus.ADD_PERIOD not defined in our system
|
// RdapStatus.AUTO_RENEW_PERIOD not defined in our system
|
||||||
// RdapStatus.AUTO_RENEW_PERIOD not defined in our system
|
.put(StatusValue.CLIENT_DELETE_PROHIBITED, RdapStatus.CLIENT_DELETE_PROHIBITED)
|
||||||
.put(StatusValue.CLIENT_DELETE_PROHIBITED, RdapStatus.CLIENT_DELETE_PROHIBITED)
|
.put(StatusValue.CLIENT_HOLD, RdapStatus.CLIENT_HOLD)
|
||||||
.put(StatusValue.CLIENT_HOLD, RdapStatus.CLIENT_HOLD)
|
.put(StatusValue.CLIENT_RENEW_PROHIBITED, RdapStatus.CLIENT_RENEW_PROHIBITED)
|
||||||
.put(StatusValue.CLIENT_RENEW_PROHIBITED, RdapStatus.CLIENT_RENEW_PROHIBITED)
|
.put(StatusValue.CLIENT_TRANSFER_PROHIBITED, RdapStatus.CLIENT_TRANSFER_PROHIBITED)
|
||||||
.put(StatusValue.CLIENT_TRANSFER_PROHIBITED, RdapStatus.CLIENT_TRANSFER_PROHIBITED)
|
.put(StatusValue.CLIENT_UPDATE_PROHIBITED, RdapStatus.CLIENT_UPDATE_PROHIBITED)
|
||||||
.put(StatusValue.CLIENT_UPDATE_PROHIBITED, RdapStatus.CLIENT_UPDATE_PROHIBITED)
|
.put(StatusValue.INACTIVE, RdapStatus.INACTIVE)
|
||||||
.put(StatusValue.INACTIVE, RdapStatus.INACTIVE)
|
.put(StatusValue.LINKED, RdapStatus.ASSOCIATED)
|
||||||
.put(StatusValue.LINKED, RdapStatus.ASSOCIATED)
|
.put(StatusValue.OK, RdapStatus.ACTIVE)
|
||||||
.put(StatusValue.OK, RdapStatus.ACTIVE)
|
.put(StatusValue.PENDING_CREATE, RdapStatus.PENDING_CREATE)
|
||||||
.put(StatusValue.PENDING_CREATE, RdapStatus.PENDING_CREATE)
|
.put(StatusValue.PENDING_DELETE, RdapStatus.PENDING_DELETE)
|
||||||
.put(StatusValue.PENDING_DELETE, RdapStatus.PENDING_DELETE)
|
// RdapStatus.PENDING_RENEW not defined in our system
|
||||||
// RdapStatus.PENDING_RENEW not defined in our system
|
// RdapStatus.PENDING_RESTORE not defined in our system
|
||||||
// RdapStatus.PENDING_RESTORE not defined in our system
|
.put(StatusValue.PENDING_TRANSFER, RdapStatus.PENDING_TRANSFER)
|
||||||
.put(StatusValue.PENDING_TRANSFER, RdapStatus.PENDING_TRANSFER)
|
.put(StatusValue.PENDING_UPDATE, RdapStatus.PENDING_UPDATE)
|
||||||
.put(StatusValue.PENDING_UPDATE, RdapStatus.PENDING_UPDATE)
|
// RdapStatus.REDEMPTION_PERIOD not defined in our system
|
||||||
// RdapStatus.REDEMPTION_PERIOD not defined in our system
|
// RdapStatus.RENEW_PERIOD not defined in our system
|
||||||
// RdapStatus.RENEW_PERIOD not defined in our system
|
.put(StatusValue.SERVER_DELETE_PROHIBITED, RdapStatus.SERVER_DELETE_PROHIBITED)
|
||||||
.put(StatusValue.SERVER_DELETE_PROHIBITED, RdapStatus.SERVER_DELETE_PROHIBITED)
|
.put(StatusValue.SERVER_HOLD, RdapStatus.SERVER_HOLD)
|
||||||
.put(StatusValue.SERVER_HOLD, RdapStatus.SERVER_HOLD)
|
.put(StatusValue.SERVER_RENEW_PROHIBITED, RdapStatus.SERVER_RENEW_PROHIBITED)
|
||||||
.put(StatusValue.SERVER_RENEW_PROHIBITED, RdapStatus.SERVER_RENEW_PROHIBITED)
|
.put(StatusValue.SERVER_TRANSFER_PROHIBITED, RdapStatus.SERVER_TRANSFER_PROHIBITED)
|
||||||
.put(StatusValue.SERVER_TRANSFER_PROHIBITED, RdapStatus.SERVER_TRANSFER_PROHIBITED)
|
.put(StatusValue.SERVER_UPDATE_PROHIBITED, RdapStatus.SERVER_UPDATE_PROHIBITED)
|
||||||
.put(StatusValue.SERVER_UPDATE_PROHIBITED, RdapStatus.SERVER_UPDATE_PROHIBITED)
|
// RdapStatus.TRANSFER_PERIOD not defined in our system
|
||||||
// RdapStatus.TRANSFER_PERIOD not defined in our system
|
.build();
|
||||||
.build());
|
|
||||||
|
|
||||||
/** Role values specified in RFC 7483 § 10.2.4. */
|
/** Role values specified in RFC 7483 § 10.2.4. */
|
||||||
private enum RdapEntityRole {
|
private enum RdapEntityRole {
|
||||||
|
@ -248,21 +250,20 @@ public class RdapJsonFormatter {
|
||||||
|
|
||||||
/** Map of EPP event values to the RDAP equivalents. */
|
/** Map of EPP event values to the RDAP equivalents. */
|
||||||
private static final ImmutableMap<HistoryEntry.Type, RdapEventAction>
|
private static final ImmutableMap<HistoryEntry.Type, RdapEventAction>
|
||||||
historyEntryTypeToRdapEventActionMap =
|
HISTORY_ENTRY_TYPE_TO_RDAP_EVENT_ACTION_MAP =
|
||||||
Maps.immutableEnumMap(
|
new ImmutableMap.Builder<HistoryEntry.Type, RdapEventAction>()
|
||||||
new ImmutableMap.Builder<HistoryEntry.Type, RdapEventAction>()
|
.put(HistoryEntry.Type.CONTACT_CREATE, RdapEventAction.REGISTRATION)
|
||||||
.put(HistoryEntry.Type.CONTACT_CREATE, RdapEventAction.REGISTRATION)
|
.put(HistoryEntry.Type.CONTACT_DELETE, RdapEventAction.DELETION)
|
||||||
.put(HistoryEntry.Type.CONTACT_DELETE, RdapEventAction.DELETION)
|
.put(HistoryEntry.Type.CONTACT_TRANSFER_APPROVE, RdapEventAction.TRANSFER)
|
||||||
.put(HistoryEntry.Type.CONTACT_TRANSFER_APPROVE, RdapEventAction.TRANSFER)
|
.put(HistoryEntry.Type.DOMAIN_AUTORENEW, RdapEventAction.REREGISTRATION)
|
||||||
.put(HistoryEntry.Type.DOMAIN_AUTORENEW, RdapEventAction.REREGISTRATION)
|
.put(HistoryEntry.Type.DOMAIN_CREATE, RdapEventAction.REGISTRATION)
|
||||||
.put(HistoryEntry.Type.DOMAIN_CREATE, RdapEventAction.REGISTRATION)
|
.put(HistoryEntry.Type.DOMAIN_DELETE, RdapEventAction.DELETION)
|
||||||
.put(HistoryEntry.Type.DOMAIN_DELETE, RdapEventAction.DELETION)
|
.put(HistoryEntry.Type.DOMAIN_RENEW, RdapEventAction.REREGISTRATION)
|
||||||
.put(HistoryEntry.Type.DOMAIN_RENEW, RdapEventAction.REREGISTRATION)
|
.put(HistoryEntry.Type.DOMAIN_RESTORE, RdapEventAction.REINSTANTIATION)
|
||||||
.put(HistoryEntry.Type.DOMAIN_RESTORE, RdapEventAction.REINSTANTIATION)
|
.put(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE, RdapEventAction.TRANSFER)
|
||||||
.put(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE, RdapEventAction.TRANSFER)
|
.put(HistoryEntry.Type.HOST_CREATE, RdapEventAction.REGISTRATION)
|
||||||
.put(HistoryEntry.Type.HOST_CREATE, RdapEventAction.REGISTRATION)
|
.put(HistoryEntry.Type.HOST_DELETE, RdapEventAction.DELETION)
|
||||||
.put(HistoryEntry.Type.HOST_DELETE, RdapEventAction.DELETION)
|
.build();
|
||||||
.build());
|
|
||||||
|
|
||||||
private static final ImmutableList<String> CONFORMANCE_LIST =
|
private static final ImmutableList<String> CONFORMANCE_LIST =
|
||||||
ImmutableList.of(RDAP_CONFORMANCE_LEVEL);
|
ImmutableList.of(RDAP_CONFORMANCE_LEVEL);
|
||||||
|
@ -598,6 +599,8 @@ public class RdapJsonFormatter {
|
||||||
}
|
}
|
||||||
ImmutableList.Builder<ImmutableMap<String, Object>> builder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<ImmutableMap<String, Object>> builder = new ImmutableList.Builder<>();
|
||||||
builder.addAll(entities);
|
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(
|
builder.add(
|
||||||
makeRdapJsonForRegistrar(
|
makeRdapJsonForRegistrar(
|
||||||
registrar.get(),
|
registrar.get(),
|
||||||
|
@ -901,12 +904,12 @@ public class RdapJsonFormatter {
|
||||||
}
|
}
|
||||||
// include the registrar contacts as subentities
|
// include the registrar contacts as subentities
|
||||||
ImmutableList<ImmutableMap<String, Object>> registrarContacts =
|
ImmutableList<ImmutableMap<String, Object>> registrarContacts =
|
||||||
registrar
|
registrar.getContacts().stream()
|
||||||
.getContacts()
|
|
||||||
.stream()
|
|
||||||
.filter(RdapJsonFormatter::isVisible)
|
|
||||||
.map(registrarContact -> makeRdapJsonForRegistrarContact(registrarContact, null))
|
.map(registrarContact -> makeRdapJsonForRegistrarContact(registrarContact, null))
|
||||||
|
.filter(entity -> !entity.isEmpty())
|
||||||
.collect(toImmutableList());
|
.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()) {
|
if (!registrarContacts.isEmpty()) {
|
||||||
jsonBuilder.put("entities", registrarContacts);
|
jsonBuilder.put("entities", registrarContacts);
|
||||||
}
|
}
|
||||||
|
@ -930,12 +933,18 @@ public class RdapJsonFormatter {
|
||||||
/**
|
/**
|
||||||
* Creates a JSON object for a {@link RegistrarContact}.
|
* 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 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
|
* @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
|
* port43 field; if null, port43 is not added to the object
|
||||||
*/
|
*/
|
||||||
static ImmutableMap<String, Object> makeRdapJsonForRegistrarContact(
|
static ImmutableMap<String, Object> makeRdapJsonForRegistrarContact(
|
||||||
RegistrarContact registrarContact, @Nullable String whoisServer) {
|
RegistrarContact registrarContact, @Nullable String whoisServer) {
|
||||||
|
ImmutableList<String> roles = makeRdapRoleList(registrarContact);
|
||||||
|
if (roles.isEmpty()) {
|
||||||
|
return ImmutableMap.of();
|
||||||
|
}
|
||||||
ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>();
|
ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>();
|
||||||
jsonBuilder.put("objectClassName", "entity");
|
jsonBuilder.put("objectClassName", "entity");
|
||||||
String gaeUserId = registrarContact.getGaeUserId();
|
String gaeUserId = registrarContact.getGaeUserId();
|
||||||
|
@ -943,7 +952,7 @@ public class RdapJsonFormatter {
|
||||||
jsonBuilder.put("handle", registrarContact.getGaeUserId());
|
jsonBuilder.put("handle", registrarContact.getGaeUserId());
|
||||||
}
|
}
|
||||||
jsonBuilder.put("status", STATUS_LIST_ACTIVE);
|
jsonBuilder.put("status", STATUS_LIST_ACTIVE);
|
||||||
jsonBuilder.put("roles", makeRdapRoleList(registrarContact));
|
jsonBuilder.put("roles", roles);
|
||||||
// Create the vCard.
|
// Create the vCard.
|
||||||
ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>();
|
||||||
vcardBuilder.add(VCARD_ENTRY_VERSION);
|
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.
|
* 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) {
|
private static ImmutableList<String> makeRdapRoleList(RegistrarContact registrarContact) {
|
||||||
ImmutableList.Builder<String> rolesBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<String> rolesBuilder = new ImmutableList.Builder<>();
|
||||||
|
@ -997,15 +1014,12 @@ public class RdapJsonFormatter {
|
||||||
if (registrarContact.getVisibleInWhoisAsTech()) {
|
if (registrarContact.getVisibleInWhoisAsTech()) {
|
||||||
rolesBuilder.add(RdapEntityRole.TECH.rfc7483String);
|
rolesBuilder.add(RdapEntityRole.TECH.rfc7483String);
|
||||||
}
|
}
|
||||||
|
if (registrarContact.getVisibleInDomainWhoisAsAbuse()) {
|
||||||
|
rolesBuilder.add(RdapEntityRole.ABUSE.rfc7483String);
|
||||||
|
}
|
||||||
return rolesBuilder.build();
|
return rolesBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks whether the registrar contact should be visible (because it has visible roles). */
|
|
||||||
private static boolean isVisible(RegistrarContact registrarContact) {
|
|
||||||
return registrarContact.getVisibleInWhoisAsAdmin()
|
|
||||||
|| registrarContact.getVisibleInWhoisAsTech();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event list for a domain, host or contact resource.
|
* Creates an event list for a domain, host or contact resource.
|
||||||
*/
|
*/
|
||||||
|
@ -1025,7 +1039,7 @@ public class RdapJsonFormatter {
|
||||||
for (HistoryEntry historyEntry :
|
for (HistoryEntry historyEntry :
|
||||||
ofy().load().type(HistoryEntry.class).ancestor(resource).order("modificationTime")) {
|
ofy().load().type(HistoryEntry.class).ancestor(resource).order("modificationTime")) {
|
||||||
RdapEventAction rdapEventAction =
|
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.
|
// Only save the historyEntries if this is a type we care about.
|
||||||
if (rdapEventAction == null) {
|
if (rdapEventAction == null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1211,7 +1225,7 @@ public class RdapJsonFormatter {
|
||||||
Stream<RdapStatus> stream =
|
Stream<RdapStatus> stream =
|
||||||
statusValues
|
statusValues
|
||||||
.stream()
|
.stream()
|
||||||
.map(status -> statusToRdapStatusMap.getOrDefault(status, RdapStatus.OBSCURED));
|
.map(status -> STATUS_TO_RDAP_STATUS_MAP.getOrDefault(status, RdapStatus.OBSCURED));
|
||||||
if (isRedacted) {
|
if (isRedacted) {
|
||||||
stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED));
|
stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED));
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,22 @@
|
||||||
],
|
],
|
||||||
"entities" :
|
"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",
|
"objectClassName" : "entity",
|
||||||
"status" : ["active"],
|
"status" : ["active"],
|
||||||
|
|
|
@ -38,6 +38,22 @@
|
||||||
],
|
],
|
||||||
"entities" :
|
"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",
|
"objectClassName" : "entity",
|
||||||
"status" : ["active"],
|
"status" : ["active"],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue