RDAP: Change data policy remark for redacted contacts

Changes the code to be in compliance with the RDAP Pilot Profile document,
which specifies:

1.4.11.  If permitted or required by an ICANN agreement provision, waiver, or Consensus Policy, an RDAP response may contain redacted registrant, administrative, technical and/or other contact information. If any information is redacted, the response MUST include a remarks member with title "Data Policy", type "object truncated due to authorization", a description containing the string "Some of the data in this object has been removed" and a links member with the elements rel:alternate and href indicating where the data policy can be found. An entity with redacted information MUST include the "removed" value in the status element.

We were using the "removed" status to indicate deleted contacts and inactive
registrars. Instead, we will now use "inactive", so that we can use "removed"
to indicated redaction.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185039201
This commit is contained in:
mountford 2018-02-08 13:06:14 -08:00 committed by jianglai
parent 178760622b
commit 85f5535811
12 changed files with 112 additions and 44 deletions

View file

@ -133,13 +133,27 @@ public class RdapIcannStandardInformation {
"type", "type",
"object truncated due to unexplainable reasons"); "object truncated due to unexplainable reasons");
/** Included when requester is not logged in as the owner of the contact being returned. */ /**
* Included when requester is not logged in as the owner of the contact being returned. Format
* required by ICANN RDAP Pilot Profile draft section 1.4.11.
*/
static final ImmutableMap<String, Object> CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK = static final ImmutableMap<String, Object> CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
ImmutableMap.of( ImmutableMap.of(
"title", "title",
"Contact Personal Data Hidden", "Data Policy",
"description", "description",
ImmutableList.of("Contact personal data is visible only to the owning registrar."), ImmutableList.of(
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar."),
"type", "type",
"object truncated due to unexplainable reasons"); "object truncated due to authorization",
"links",
ImmutableList.of(
ImmutableMap.of(
"value",
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"rel", "alternate",
"href",
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"type", "text/html")));
} }

View file

@ -272,8 +272,8 @@ public class RdapJsonFormatter {
private static final ImmutableList<String> STATUS_LIST_ACTIVE = private static final ImmutableList<String> STATUS_LIST_ACTIVE =
ImmutableList.of(RdapStatus.ACTIVE.rfc7483String); ImmutableList.of(RdapStatus.ACTIVE.rfc7483String);
private static final ImmutableList<String> STATUS_LIST_REMOVED = private static final ImmutableList<String> STATUS_LIST_INACTIVE =
ImmutableList.of(RdapStatus.REMOVED.rfc7483String); ImmutableList.of(RdapStatus.INACTIVE.rfc7483String);
private static final ImmutableMap<String, ImmutableList<String>> PHONE_TYPE_VOICE = private static final ImmutableMap<String, ImmutableList<String>> PHONE_TYPE_VOICE =
ImmutableMap.of("type", ImmutableList.of("voice")); ImmutableMap.of("type", ImmutableList.of("voice"));
private static final ImmutableMap<String, ImmutableList<String>> PHONE_TYPE_FAX = private static final ImmutableMap<String, ImmutableList<String>> PHONE_TYPE_FAX =
@ -496,7 +496,9 @@ public class RdapJsonFormatter {
jsonBuilder.put( jsonBuilder.put(
"status", "status",
makeStatusValueList( makeStatusValueList(
domainResource.getStatusValues(), domainResource.getDeletionTime().isBefore(now))); domainResource.getStatusValues(),
false, // isRedacted
domainResource.getDeletionTime().isBefore(now)));
jsonBuilder.put("links", ImmutableList.of( jsonBuilder.put("links", ImmutableList.of(
makeLink("domain", domainResource.getFullyQualifiedDomainName(), linkBase))); makeLink("domain", domainResource.getFullyQualifiedDomainName(), linkBase)));
boolean displayContacts = boolean displayContacts =
@ -612,7 +614,10 @@ public class RdapJsonFormatter {
} }
jsonBuilder.put( jsonBuilder.put(
"status", "status",
makeStatusValueList(statuses.build(), hostResource.getDeletionTime().isBefore(now))); makeStatusValueList(
statuses.build(),
false, // isRedacted
hostResource.getDeletionTime().isBefore(now)));
jsonBuilder.put("links", ImmutableList.of( jsonBuilder.put("links", ImmutableList.of(
makeLink("nameserver", hostResource.getFullyQualifiedHostName(), linkBase))); makeLink("nameserver", hostResource.getFullyQualifiedHostName(), linkBase)));
List<ImmutableMap<String, Object>> remarks; List<ImmutableMap<String, Object>> remarks;
@ -691,6 +696,8 @@ public class RdapJsonFormatter {
DateTime now, DateTime now,
OutputDataType outputDataType, OutputDataType outputDataType,
RdapAuthorization authorization) { RdapAuthorization authorization) {
boolean isAuthorized =
authorization.isAuthorizedForClientId(contactResource.getCurrentSponsorClientId());
ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>(); ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>();
ImmutableList.Builder<ImmutableMap<String, Object>> remarksBuilder ImmutableList.Builder<ImmutableMap<String, Object>> remarksBuilder
= new ImmutableList.Builder<>(); = new ImmutableList.Builder<>();
@ -702,13 +709,14 @@ public class RdapJsonFormatter {
isLinked(Key.create(contactResource), now) isLinked(Key.create(contactResource), now)
? union(contactResource.getStatusValues(), StatusValue.LINKED) ? union(contactResource.getStatusValues(), StatusValue.LINKED)
: contactResource.getStatusValues(), : contactResource.getStatusValues(),
!isAuthorized,
contactResource.getDeletionTime().isBefore(now))); contactResource.getDeletionTime().isBefore(now)));
contactType.ifPresent( contactType.ifPresent(
type -> jsonBuilder.put("roles", ImmutableList.of(convertContactTypeToRdapRole(type)))); type -> jsonBuilder.put("roles", ImmutableList.of(convertContactTypeToRdapRole(type))));
jsonBuilder.put("links", jsonBuilder.put("links",
ImmutableList.of(makeLink("entity", contactResource.getRepoId(), linkBase))); ImmutableList.of(makeLink("entity", contactResource.getRepoId(), linkBase)));
// If we are logged in as the owner of this contact, create the vCard. // If we are logged in as the owner of this contact, create the vCard.
if (authorization.isAuthorizedForClientId(contactResource.getCurrentSponsorClientId())) { if (isAuthorized) {
ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>();
vcardBuilder.add(VCARD_ENTRY_VERSION); vcardBuilder.add(VCARD_ENTRY_VERSION);
PostalInfo postalInfo = contactResource.getInternationalizedPostalInfo(); PostalInfo postalInfo = contactResource.getInternationalizedPostalInfo();
@ -794,7 +802,7 @@ public class RdapJsonFormatter {
jsonBuilder.put("objectClassName", "entity"); jsonBuilder.put("objectClassName", "entity");
Long ianaIdentifier = registrar.getIanaIdentifier(); Long ianaIdentifier = registrar.getIanaIdentifier();
jsonBuilder.put("handle", (ianaIdentifier == null) ? "(none)" : ianaIdentifier.toString()); jsonBuilder.put("handle", (ianaIdentifier == null) ? "(none)" : ianaIdentifier.toString());
jsonBuilder.put("status", registrar.isLive() ? STATUS_LIST_ACTIVE : STATUS_LIST_REMOVED); jsonBuilder.put("status", registrar.isLive() ? STATUS_LIST_ACTIVE : STATUS_LIST_INACTIVE);
jsonBuilder.put("roles", ImmutableList.of(RdapEntityRole.REGISTRAR.rfc7483String)); jsonBuilder.put("roles", ImmutableList.of(RdapEntityRole.REGISTRAR.rfc7483String));
if (ianaIdentifier != null) { if (ianaIdentifier != null) {
jsonBuilder.put("links", jsonBuilder.put("links",
@ -1086,20 +1094,24 @@ public class RdapJsonFormatter {
/** /**
* Creates a string array of status values. * Creates a string array of status values.
* *
* <p>The spec indicates that OK should be listed as "active". We use the "removed" status to * <p>The spec indicates that OK should be listed as "active". We use the "inactive" status to
* indicate deleted objects. * indicate deleted objects, and as directed by the profile, the "removed" status to indicate
* redacted objects.
*/ */
private static ImmutableList<String> makeStatusValueList( private static ImmutableList<String> makeStatusValueList(
ImmutableSet<StatusValue> statusValues, boolean isDeleted) { ImmutableSet<StatusValue> statusValues, boolean isRedacted, boolean isDeleted) {
Stream<RdapStatus> stream = Stream<RdapStatus> stream =
statusValues statusValues
.stream() .stream()
.map(status -> statusToRdapStatusMap.getOrDefault(status, RdapStatus.OBSCURED)); .map(status -> statusToRdapStatusMap.getOrDefault(status, RdapStatus.OBSCURED));
if (isRedacted) {
stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED));
}
if (isDeleted) { if (isDeleted) {
stream = stream =
Streams.concat( Streams.concat(
stream.filter(rdapStatus -> !Objects.equals(rdapStatus, RdapStatus.ACTIVE)), stream.filter(rdapStatus -> !Objects.equals(rdapStatus, RdapStatus.ACTIVE)),
Stream.of(RdapStatus.REMOVED)); Stream.of(RdapStatus.INACTIVE));
} }
return stream return stream
.map(RdapStatus::getDisplayName) .map(RdapStatus::getDisplayName)

View file

@ -409,7 +409,7 @@ public class RdapEntityActionTest {
runSuccessfulTest( runSuccessfulTest(
deletedContact.getRepoId(), deletedContact.getRepoId(),
"", "",
"removed", "inactive",
"", "",
false, false,
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -422,7 +422,7 @@ public class RdapEntityActionTest {
runSuccessfulTest( runSuccessfulTest(
deletedContact.getRepoId(), deletedContact.getRepoId(),
"", "",
"removed", "inactive",
"", "",
false, false,
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -471,7 +471,7 @@ public class RdapEntityActionTest {
login("deletedregistrar"); login("deletedregistrar");
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
runSuccessfulTest( runSuccessfulTest(
"104", "Yes Virginia <script>", "removed", null, false, "rdap_registrar.json"); "104", "Yes Virginia <script>", "inactive", null, false, "rdap_registrar.json");
} }
@Test @Test
@ -486,7 +486,7 @@ public class RdapEntityActionTest {
loginAsAdmin(); loginAsAdmin();
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
runSuccessfulTest( runSuccessfulTest(
"104", "Yes Virginia <script>", "removed", null, false, "rdap_registrar.json"); "104", "Yes Virginia <script>", "inactive", null, false, "rdap_registrar.json");
} }
@Test @Test

View file

@ -946,7 +946,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
public void testNameMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception { public void testNameMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact"); login("2-RegistrarInact");
runSuccessfulNameTest("No Way", "21", "No Way", "removed", null, null, "rdap_registrar.json"); runSuccessfulNameTest("No Way", "21", "No Way", "inactive", null, null, "rdap_registrar.json");
verifyMetrics(0); verifyMetrics(0);
} }
@ -954,7 +954,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
public void testNameMatchRegistrar_found_inactiveAsAdmin() throws Exception { public void testNameMatchRegistrar_found_inactiveAsAdmin() throws Exception {
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
loginAsAdmin(); loginAsAdmin();
runSuccessfulNameTest("No Way", "21", "No Way", "removed", null, null, "rdap_registrar.json"); runSuccessfulNameTest("No Way", "21", "No Way", "inactive", null, null, "rdap_registrar.json");
verifyMetrics(0); verifyMetrics(0);
} }
@ -1059,7 +1059,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
"6-ROID", "6-ROID",
"6-ROID", "6-ROID",
"", "",
"removed", "inactive",
"", "",
"", "",
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -1074,7 +1074,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
"6-ROID", "6-ROID",
"6-ROID", "6-ROID",
"", "",
"removed", "inactive",
"", "",
"", "",
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -1106,7 +1106,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
"6-ROI*", "6-ROI*",
"6-ROID", "6-ROID",
"", "",
"removed", "inactive",
"", "",
"", "",
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -1121,7 +1121,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
"6-ROI*", "6-ROI*",
"6-ROID", "6-ROID",
"", "",
"removed", "inactive",
"", "",
"", "",
"rdap_contact_deleted.json"); "rdap_contact_deleted.json");
@ -1321,7 +1321,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
public void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception { public void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact"); login("2-RegistrarInact");
runSuccessfulHandleTest("21", "21", "No Way", "removed", null, null, "rdap_registrar.json"); runSuccessfulHandleTest("21", "21", "No Way", "inactive", null, null, "rdap_registrar.json");
verifyMetrics(0); verifyMetrics(0);
} }
@ -1329,7 +1329,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase {
public void testHandleMatchRegistrar_found_inactiveAsAdmin() throws Exception { public void testHandleMatchRegistrar_found_inactiveAsAdmin() throws Exception {
action.includeDeletedParam = Optional.of(true); action.includeDeletedParam = Optional.of(true);
loginAsAdmin(); loginAsAdmin();
runSuccessfulHandleTest("21", "21", "No Way", "removed", null, null, "rdap_registrar.json"); runSuccessfulHandleTest("21", "21", "No Way", "inactive", null, null, "rdap_registrar.json");
verifyMetrics(0); verifyMetrics(0);
} }
} }

View file

@ -384,7 +384,7 @@ public class RdapNameserverActionTest {
"HANDLE", "A-ROID", "HANDLE", "A-ROID",
"ADDRESSTYPE", "v4", "ADDRESSTYPE", "v4",
"ADDRESS", "1.2.3.4", "ADDRESS", "1.2.3.4",
"STATUS", "removed"), "STATUS", "inactive"),
"rdap_host.json")); "rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getStatus()).isEqualTo(200);
} }
@ -407,7 +407,7 @@ public class RdapNameserverActionTest {
"HANDLE", "A-ROID", "HANDLE", "A-ROID",
"ADDRESSTYPE", "v4", "ADDRESSTYPE", "v4",
"ADDRESS", "1.2.3.4", "ADDRESS", "1.2.3.4",
"STATUS", "removed"), "STATUS", "inactive"),
"rdap_host.json")); "rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getStatus()).isEqualTo(200);
} }
@ -425,7 +425,7 @@ public class RdapNameserverActionTest {
"HANDLE", "A-ROID", "HANDLE", "A-ROID",
"ADDRESSTYPE", "v4", "ADDRESSTYPE", "v4",
"ADDRESS", "1.2.3.4", "ADDRESS", "1.2.3.4",
"STATUS", "removed"), "STATUS", "inactive"),
"rdap_host.json")); "rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getStatus()).isEqualTo(200);
} }

View file

@ -69,11 +69,23 @@ public class RdapTestHelper {
case CONTACT: case CONTACT:
noticesBuilder.add( noticesBuilder.add(
ImmutableMap.of( ImmutableMap.of(
"title", "Contact Personal Data Hidden", "title", "Data Policy",
"description", "description",
ImmutableList.of( ImmutableList.of(
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar."), "Contact personal data is visible only to the owning registrar."),
"type", "object truncated due to unexplainable reasons")); "type", "object truncated due to authorization",
"links",
ImmutableList.of(
ImmutableMap.of(
"value",
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"rel",
"alternate",
"href",
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"type",
"text/html"))));
break; break;
default: default:
break; break;

View file

@ -1,7 +1,7 @@
{ {
"objectClassName" : "entity", "objectClassName" : "entity",
"handle" : "%NAME%", "handle" : "%NAME%",
"status" : ["active", "associated"], "status" : ["active", "associated", "removed"],
"links" : "links" :
[ [
{ {

View file

@ -1,7 +1,7 @@
{ {
"objectClassName" : "entity", "objectClassName" : "entity",
"handle" : "%NAME%", "handle" : "%NAME%",
"status" : ["removed"], "status" : ["inactive"],
"links" : "links" :
[ [
{ {

View file

@ -1,7 +1,7 @@
{ {
"objectClassName" : "entity", "objectClassName" : "entity",
"handle" : "%NAME%", "handle" : "%NAME%",
"status" : ["active"], "status" : ["active", "removed"],
"links" : "links" :
[ [
{ {
@ -24,11 +24,21 @@
], ],
"remarks": [ "remarks": [
{ {
"title": "Contact Personal Data Hidden", "title": "Data Policy",
"description": [ "description": [
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar." "Contact personal data is visible only to the owning registrar."
], ],
"type": "object truncated due to unexplainable reasons" "type": "object truncated due to authorization",
"links" :
[
{
"value" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"rel" : "alternate",
"href" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"type" : "text/html"
}
]
} }
] ]
} }

View file

@ -3,7 +3,7 @@
"client delete prohibited", "client delete prohibited",
"client renew prohibited", "client renew prohibited",
"client transfer prohibited", "client transfer prohibited",
"removed", "inactive",
"server update prohibited" "server update prohibited"
], ],
"handle": "%HANDLE%", "handle": "%HANDLE%",

View file

@ -1,7 +1,7 @@
{ {
"objectClassName" : "entity", "objectClassName" : "entity",
"handle" : "2-ROID", "handle" : "2-ROID",
"status" : ["active", "associated"], "status" : ["active", "associated", "removed"],
"roles" : ["registrant"], "roles" : ["registrant"],
"links" : "links" :
[ [
@ -25,11 +25,21 @@
], ],
"remarks": [ "remarks": [
{ {
"title": "Contact Personal Data Hidden", "title": "Data Policy",
"description": [ "description": [
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar." "Contact personal data is visible only to the owning registrar."
], ],
"type": "object truncated due to unexplainable reasons" "type": "object truncated due to authorization",
"links" :
[
{
"value" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"rel" : "alternate",
"href" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"type" : "text/html"
}
]
} }
] ]
} }

View file

@ -33,11 +33,21 @@
"type": "object truncated due to unexplainable reasons" "type": "object truncated due to unexplainable reasons"
}, },
{ {
"title": "Contact Personal Data Hidden", "title": "Data Policy",
"description": [ "description": [
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar." "Contact personal data is visible only to the owning registrar."
], ],
"type": "object truncated due to unexplainable reasons" "type": "object truncated due to authorization",
"links" :
[
{
"value" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"rel" : "alternate",
"href" : "https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
"type" : "text/html"
}
]
} }
] ]
} }