Add RDAP support for deleted contacts and registrars

This CL adds the functionality for contact and registrar searches. A future CL will handle domains and entities.

Support is also added for filtering results by registrar.

Deleted items can only be seen by admins, and by registrars viewing their own deleted items.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=170734664
This commit is contained in:
mountford 2017-10-02 12:20:04 -07:00 committed by Ben McIlwain
parent 6740e9270f
commit a5c931a152
13 changed files with 905 additions and 375 deletions

View file

@ -85,7 +85,7 @@ public class RdapEntityAction extends RdapActionBase {
ContactResource contactResource = ofy().load().key(contactKey).now();
// As per Andy Newton on the regext mailing list, contacts by themselves have no role, since
// they are global, and might have different roles for different domains.
if ((contactResource != null) && now.isBefore(contactResource.getDeletionTime())) {
if ((contactResource != null) && shouldBeVisible(contactResource, now)) {
return rdapJsonFormatter.makeRdapJsonForContact(
contactResource,
true,
@ -101,7 +101,7 @@ public class RdapEntityAction extends RdapActionBase {
if (ianaIdentifier != null) {
wasValidKey = true;
Optional<Registrar> registrar = getRegistrarByIanaIdentifier(ianaIdentifier);
if ((registrar.isPresent()) && registrar.get().isLiveAndPubliclyVisible()) {
if (registrar.isPresent() && shouldBeVisible(registrar.get())) {
return rdapJsonFormatter.makeRdapJsonForRegistrar(
registrar.get(), true, rdapLinkBase, rdapWhoisServer, now, OutputDataType.FULL);
}
@ -112,4 +112,3 @@ public class RdapEntityAction extends RdapActionBase {
: new BadRequestException(pathSearchString + " is not a valid entity handle");
}
}

View file

@ -18,7 +18,6 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
@ -27,7 +26,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Longs;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.registrar.Registrar;
@ -65,6 +64,8 @@ public class RdapEntitySearchAction extends RdapActionBase {
public static final String PATH = "/rdap/entities";
private static final int RESULT_SET_SIZE_SCALING_FACTOR = 30;
@Inject Clock clock;
@Inject @Parameter("fn") Optional<String> fnParam;
@Inject @Parameter("handle") Optional<String> handleParam;
@ -124,134 +125,167 @@ public class RdapEntitySearchAction extends RdapActionBase {
* by registrar contact name:
*
* <p>The search is by registrar name only. The profile is supporting the functionality defined
* in the Base Registry Agreement (see 1.6 of Section 4 of the Base Registry Agreement,
* https://newgtlds.icann.org/sites/default/files/agreements/
* agreement-approved-09jan14-en.htm).
* in the Base Registry Agreement.
*
* <p>According to RFC 7482 section 6.1, punycode is only used for domain name labels, so we can
* assume that entity names are regular unicode.
*
* <p>Searches for deleted entities are treated like wildcard searches, because they can return
* multiple entities.
*
* @see <a href="https://newgtlds.icann.org/sites/default/files/agreements/agreement-approved-09jan14-en.htm">1.6
* of Section 4 of the Base Registry Agreement</a>
*/
private RdapSearchResults searchByName(final RdapSearchPattern partialStringQuery, DateTime now) {
// For wildcard searches, make sure the initial string is long enough, and don't allow suffixes.
if (partialStringQuery.getHasWildcard()) {
if (partialStringQuery.getSuffix() != null) {
// For wildcard searches, and searches that include deleted items, make sure the initial string
// is long enough, and don't allow suffixes.
if ((partialStringQuery.getHasWildcard() || shouldIncludeDeleted())
&& (partialStringQuery.getSuffix() != null)) {
throw new UnprocessableEntityException(
"Suffixes not allowed in wildcard entity name searches");
partialStringQuery.getHasWildcard()
? "Suffixes not allowed in wildcard entity name searches"
: "Suffixes not allowed when searching for deleted entities");
}
if (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
if (partialStringQuery.getHasWildcard()
&& (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
throw new UnprocessableEntityException(
"Initial search string required in wildcard entity name searches");
partialStringQuery.getHasWildcard()
? "Initial search string required in wildcard entity name searches"
: "Initial search string required when searching for deleted entities");
}
}
// Get the registrar matches, depending on whether there's a wildcard.
ImmutableList<Registrar> registrarMatches =
// Get the registrar matches.
ImmutableList<Registrar> registrars =
FluentIterable.from(Registrar.loadAllCached())
.filter(
new Predicate<Registrar>() {
@Override
public boolean apply(Registrar registrar) {
return partialStringQuery.matches(registrar.getRegistrarName());
}})
return partialStringQuery.matches(registrar.getRegistrarName())
&& shouldBeVisible(registrar);
}
})
.limit(rdapResultSetMaxSize + 1)
.toList();
// Get the contact matches and return the results, fetching an additional contact to detect
// truncation.
return makeSearchResults(
// truncation. If we are including deleted entries, we must fetch more entries, in case some
// get excluded due to permissioning.
Query<ContactResource> query =
queryItems(
ContactResource.class,
"searchName",
partialStringQuery,
false /* includeDeleted */,
rdapResultSetMaxSize + 1)
.list(),
registrarMatches,
now);
shouldIncludeDeleted(),
shouldIncludeDeleted()
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
: (rdapResultSetMaxSize + 1));
return makeSearchResults(getMatchingResources(query, now), registrars, now);
}
/** Searches for entities by handle, returning a JSON array of entity info maps. */
/**
* Searches for entities by handle, returning a JSON array of entity info maps.
*
* <p>Searches for deleted entities are treated like wildcard searches.
*
* <p>We don't allow suffixes after a wildcard in entity searches. Suffixes are used in domain
* searches to specify a TLD, and in nameserver searches to specify an in-bailiwick domain name.
* In both cases, the suffix can be turned into an additional query filter field. For contacts,
* there is no equivalent string suffix that can be used as a query filter, so we disallow use.
*/
private RdapSearchResults searchByHandle(
final RdapSearchPattern partialStringQuery, DateTime now) {
// Handle queries without a wildcard -- load by ID.
if (!partialStringQuery.getHasWildcard()) {
if (partialStringQuery.getSuffix() != null) {
throw new UnprocessableEntityException("Suffixes not allowed in entity handle searches");
}
// Handle queries without a wildcard (and not including deleted) -- load by ID.
if (!partialStringQuery.getHasWildcard() && !shouldIncludeDeleted()) {
ContactResource contactResource = ofy().load()
.type(ContactResource.class)
.id(partialStringQuery.getInitialString())
.now();
ImmutableList<Registrar> registrars =
getMatchingRegistrars(partialStringQuery.getInitialString());
return makeSearchResults(
((contactResource == null) || !contactResource.getDeletionTime().isEqual(END_OF_TIME))
? ImmutableList.<ContactResource>of() : ImmutableList.of(contactResource),
registrars,
((contactResource != null) && shouldBeVisible(contactResource, now))
? ImmutableList.of(contactResource)
: ImmutableList.<ContactResource>of(),
IncompletenessWarningType.NONE,
getMatchingRegistrars(partialStringQuery.getInitialString()),
now);
// Handle queries with a wildcard, but no suffix. For contact resources, the deletion time will
// always be END_OF_TIME for non-deleted records; unlike domain resources, we don't need to
// worry about deletion times in the future. That allows us to use an equality query for the
// deletion time. Because the handle for registrars is the IANA identifier number, don't allow
// wildcard searches for registrars, by simply not searching for registrars if a wildcard is
// present. Fetch an extra contact to detect result set truncation.
} else if (partialStringQuery.getSuffix() == null) {
if (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
throw new UnprocessableEntityException(
"Initial search string required in wildcard entity handle searches");
}
return makeSearchResults(
ofy().load()
.type(ContactResource.class)
.filterKey(
">=", Key.create(ContactResource.class, partialStringQuery.getInitialString()))
.filterKey(
"<", Key.create(ContactResource.class, partialStringQuery.getNextInitialString()))
.filter("deletionTime", END_OF_TIME)
.limit(rdapResultSetMaxSize + 1)
.list(),
ImmutableList.<Registrar>of(),
now);
// Don't allow suffixes in entity handle search queries.
// Handle queries with a wildcard (or including deleted), but no suffix. Because the handle
// for registrars is the IANA identifier number, don't allow wildcard searches for registrars,
// by simply not searching for registrars if a wildcard is present. Fetch an extra contact to
// detect result set truncation.
} else {
throw new UnprocessableEntityException("Suffixes not allowed in entity handle searches");
ImmutableList<Registrar> registrars =
partialStringQuery.getHasWildcard()
? ImmutableList.<Registrar>of()
: getMatchingRegistrars(partialStringQuery.getInitialString());
// Get the contact matches and return the results, fetching an additional contact to detect
// truncation. If we are including deleted entries, we must fetch more entries, in case some
// get excluded due to permissioning.
Query<ContactResource> query =
queryItemsByKey(
ContactResource.class,
partialStringQuery,
shouldIncludeDeleted(),
shouldIncludeDeleted()
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
: (rdapResultSetMaxSize + 1));
return makeSearchResults(getMatchingResources(query, now), registrars, now);
}
}
/** Looks up registrars by handle (i.e. IANA identifier). */
private ImmutableList<Registrar> getMatchingRegistrars(final String ianaIdentifierString) {
Long ianaIdentifier = Longs.tryParse(ianaIdentifierString);
if (ianaIdentifier != null) {
Optional<Registrar> registrar = getRegistrarByIanaIdentifier(ianaIdentifier);
if (registrar.isPresent()) {
return ImmutableList.of(registrar.get());
}
}
if (ianaIdentifier == null) {
return ImmutableList.of();
}
Optional<Registrar> registrar = getRegistrarByIanaIdentifier(ianaIdentifier);
return (registrar.isPresent() && shouldBeVisible(registrar.get()))
? ImmutableList.of(registrar.get())
: ImmutableList.<Registrar>of();
}
/** Builds a JSON array of entity info maps based on the specified contacts and registrars. */
/**
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
*
* <p>This is a convenience wrapper for the four-argument makeSearchResults; it unpacks the two
* properties of the ContactsAndIncompletenessWarningType structure and passes them as separate
* arguments.
*/
private RdapSearchResults makeSearchResults(
List<ContactResource> contacts, List<Registrar> registrars, DateTime now) {
RdapResourcesAndIncompletenessWarningType<ContactResource>
resourcesAndIncompletenessWarningType,
List<Registrar> registrars,
DateTime now) {
return makeSearchResults(
resourcesAndIncompletenessWarningType.resources(),
resourcesAndIncompletenessWarningType.incompletenessWarningType(),
registrars,
now);
}
/**
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
*
* @param contacts the list of contacts which can be returned
* @param incompletenessWarningType MIGHT_BE_INCOMPLETE if the list of contacts might be
* incomplete; this only matters if the total count of contacts and registrars combined is
* less than a full result set's worth
* @param registrars the list of registrars which can be returned
* @param now the current date and time
* @return an {@link RdapSearchResults} object
*/
private RdapSearchResults makeSearchResults(
List<ContactResource> contacts,
IncompletenessWarningType incompletenessWarningType,
List<Registrar> registrars,
DateTime now) {
// Determine what output data type to use, depending on whether more than one entity will be
// returned.
int numEntities = contacts.size();
OutputDataType outputDataType;
// If there's more than one contact, then we know already we need SUMMARY mode.
if (numEntities > 1) {
outputDataType = OutputDataType.SUMMARY;
// If there are fewer than two contacts, loop through and compute the total number of contacts
// and registrars, stopping as soon as we find two.
} else {
outputDataType = OutputDataType.FULL;
for (Registrar registrar : registrars) {
if (registrar.isLiveAndPubliclyVisible()) {
numEntities++;
if (numEntities > 1) {
outputDataType = OutputDataType.SUMMARY;
break;
}
}
}
}
OutputDataType outputDataType =
(contacts.size() + registrars.size() > 1) ? OutputDataType.SUMMARY : OutputDataType.FULL;
// There can be more results than our max size, partially because we have two pools to draw from
// (contacts and registrars), and partially because we try to fetch one more than the max size,
@ -276,7 +310,6 @@ public class RdapEntitySearchAction extends RdapActionBase {
authorization));
}
for (Registrar registrar : registrars) {
if (registrar.isLiveAndPubliclyVisible()) {
if (jsonOutputList.size() >= rdapResultSetMaxSize) {
return RdapSearchResults.create(
ImmutableList.copyOf(jsonOutputList), IncompletenessWarningType.TRUNCATED);
@ -284,7 +317,10 @@ public class RdapEntitySearchAction extends RdapActionBase {
jsonOutputList.add(rdapJsonFormatter.makeRdapJsonForRegistrar(
registrar, false, rdapLinkBase, rdapWhoisServer, now, outputDataType));
}
}
return RdapSearchResults.create(ImmutableList.copyOf(jsonOutputList));
return RdapSearchResults.create(
ImmutableList.copyOf(jsonOutputList),
(jsonOutputList.size() < rdapResultSetMaxSize)
? incompletenessWarningType
: IncompletenessWarningType.NONE);
}
}

View file

@ -767,16 +767,19 @@ public class RdapJsonFormatter {
OutputDataType outputDataType) {
ImmutableMap.Builder<String, Object> jsonBuilder = new ImmutableMap.Builder<>();
jsonBuilder.put("objectClassName", "entity");
jsonBuilder.put("handle", registrar.getIanaIdentifier().toString());
Long ianaIdentifier = registrar.getIanaIdentifier();
jsonBuilder.put("handle", (ianaIdentifier == null) ? "(none)" : ianaIdentifier.toString());
jsonBuilder.put("status", registrar.isLive() ? STATUS_LIST_ACTIVE : STATUS_LIST_REMOVED);
jsonBuilder.put("roles", ImmutableList.of(RdapEntityRole.REGISTRAR.rfc7483String));
if (ianaIdentifier != null) {
jsonBuilder.put("links",
ImmutableList.of(makeLink("entity", registrar.getIanaIdentifier().toString(), linkBase)));
jsonBuilder.put("publicIds",
ImmutableList.of(makeLink("entity", ianaIdentifier.toString(), linkBase)));
jsonBuilder.put(
"publicIds",
ImmutableList.of(
ImmutableMap.of(
"type", "IANA Registrar ID",
"identifier", registrar.getIanaIdentifier().toString())));
"type", "IANA Registrar ID", "identifier", ianaIdentifier.toString())));
}
// Create the vCard.
ImmutableList.Builder<Object> vcardBuilder = new ImmutableList.Builder<>();
vcardBuilder.add(VCARD_ENTRY_VERSION);
@ -964,9 +967,10 @@ public class RdapJsonFormatter {
*/
private static ImmutableList<Object> makeEvents(Registrar registrar, DateTime now) {
ImmutableList.Builder<Object> eventsBuilder = new ImmutableList.Builder<>();
Long ianaIdentifier = registrar.getIanaIdentifier();
eventsBuilder.add(makeEvent(
RdapEventAction.REGISTRATION,
registrar.getIanaIdentifier().toString(),
(ianaIdentifier == null) ? "(none)" : ianaIdentifier.toString(),
registrar.getCreationTime()));
if ((registrar.getLastUpdateTime() != null)
&& registrar.getLastUpdateTime().isAfter(registrar.getCreationTime())) {

View file

@ -19,7 +19,6 @@ import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResources;
import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistContactResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeContactResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeDomainResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeHostResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar;
@ -29,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.appengine.api.users.User;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@ -45,6 +45,7 @@ import google.registry.testing.FakeResponse;
import google.registry.testing.InjectRule;
import google.registry.ui.server.registrar.SessionUtils;
import java.util.Map;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
import org.json.simple.JSONValue;
@ -72,6 +73,7 @@ public class RdapEntityActionTest {
private final SessionUtils sessionUtils = mock(SessionUtils.class);
private final User user = new User("rdap.user@example.com", "gmail.com", "12345");
UserAuthInfo userAuthInfo = UserAuthInfo.create(user, false);
UserAuthInfo adminUserAuthInfo = UserAuthInfo.create(user, true);
private RdapEntityAction action;
@ -132,32 +134,50 @@ public class RdapEntityActionTest {
Registrar registrar1tld = persistResource(
makeRegistrar("1tldregistrar", "Multilevel Registrar", Registrar.State.ACTIVE, 103L));
persistSimpleResources(makeRegistrarContacts(registrar1tld));
// deleted registrar
Registrar registrarDeleted = persistResource(
makeRegistrar("deletedregistrar", "Yes Virginia <script>", Registrar.State.PENDING, 104L));
persistSimpleResources(makeRegistrarContacts(registrarDeleted));
// other contacts
disconnectedContact = makeAndPersistContactResource(
disconnectedContact =
makeAndPersistContactResource(
"8372808-DIS",
"(◕‿◕)",
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
clock.nowUtc(),
registrarLol);
deletedContact = persistResource(makeContactResource(
deletedContact =
makeAndPersistContactResource(
"8372808-DEL",
"(◕‿◕)",
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
registrarLol)
.asBuilder().setDeletionTime(clock.nowUtc()).build());
ImmutableList.of("2 Smiley Row"),
clock.nowUtc().minusYears(1),
registrarLol,
clock.nowUtc().minusMonths(6));
action = new RdapEntityAction();
action.clock = clock;
action.request = request;
action.response = response;
action.registrarParam = Optional.<String>absent();
action.includeDeletedParam = Optional.<Boolean>absent();
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.com/rdap/";
action.rdapWhoisServer = null;
action.sessionUtils = sessionUtils;
action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo);
}
private void login(String registrar) {
when(sessionUtils.checkRegistrarConsoleLogin(request, userAuthInfo)).thenReturn(true);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("evilregistrar");
when(sessionUtils.getRegistrarClientId(request)).thenReturn(registrar);
}
private void loginAsAdmin() {
action.authResult = AuthResult.create(AuthLevel.USER, adminUserAuthInfo);
when(sessionUtils.checkRegistrarConsoleLogin(request, adminUserAuthInfo)).thenReturn(true);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("irrelevant");
}
private Object generateActualJson(String name) {
@ -166,31 +186,61 @@ public class RdapEntityActionTest {
return JSONValue.parse(response.getPayload());
}
private Object generateExpectedJson(String handle, String expectedOutputFile) {
return generateExpectedJson(handle, "(◕‿◕)", "", expectedOutputFile);
}
private Object generateExpectedJson(
String handle,
String status,
String expectedOutputFile) {
return JSONValue.parse(loadFileWithSubstitutions(
return generateExpectedJson(handle, "(◕‿◕)", status, expectedOutputFile);
}
private Object generateExpectedJson(
String handle,
String fullName,
String status,
String expectedOutputFile) {
return generateExpectedJson(
handle, fullName, status, null, expectedOutputFile);
}
private Object generateExpectedJson(
String handle,
String fullName,
String status,
@Nullable String address,
String expectedOutputFile) {
return JSONValue.parse(
loadFileWithSubstitutions(
this.getClass(),
expectedOutputFile,
ImmutableMap.of(
"NAME", handle,
"FULLNAME", "(◕‿◕)",
"ADDRESS", "\"1 Smiley Row\", \"Suite みんな\"",
"EMAIL", "lol@cat.みんな",
"TYPE", "entity")));
new ImmutableMap.Builder<String, String>()
.put("NAME", handle)
.put("FULLNAME", fullName)
.put("ADDRESS", (address == null) ? "\"1 Smiley Row\", \"Suite みんな\"" : address)
.put("EMAIL", "lol@cat.みんな")
.put("TYPE", "entity")
.put("STATUS", status)
.build()));
}
private Object generateExpectedJsonWithTopLevelEntries(
String handle,
String expectedOutputFile) {
return generateExpectedJsonWithTopLevelEntries(handle, false, expectedOutputFile);
return generateExpectedJsonWithTopLevelEntries(
handle, "(◕‿◕)", "active", null, false, expectedOutputFile);
}
private Object generateExpectedJsonWithTopLevelEntries(
String handle,
String fullName,
String status,
String address,
boolean addNoPersonalDataRemark,
String expectedOutputFile) {
Object obj = generateExpectedJson(handle, expectedOutputFile);
Object obj = generateExpectedJson(handle, fullName, status, address, expectedOutputFile);
if (obj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) obj;
@ -211,6 +261,34 @@ public class RdapEntityActionTest {
return obj;
}
private void runSuccessfulTest(String queryString, String fileName) {
runSuccessfulTest(queryString, "(◕‿◕)", "active", null, false, fileName);
}
private void runSuccessfulTest(String queryString, String fullName, String fileName) {
runSuccessfulTest(queryString, fullName, "active", null, false, fileName);
}
private void runSuccessfulTest(
String queryString,
String fullName,
String rdapStatus,
String address,
boolean addNoPersonalDataRemark,
String fileName) {
assertThat(generateActualJson(queryString))
.isEqualTo(
generateExpectedJsonWithTopLevelEntries(
queryString, fullName, rdapStatus, address, addNoPersonalDataRemark, fileName));
assertThat(response.getStatus()).isEqualTo(200);
}
private void runNotFoundTest(String queryString) {
assertThat(generateActualJson(queryString))
.isEqualTo(generateExpectedJson(queryString + " not found", "", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
public void testInvalidEntity_returns400() throws Exception {
assertThat(generateActualJson("invalid/entity/handle")).isEqualTo(
@ -221,108 +299,196 @@ public class RdapEntityActionTest {
}
@Test
public void testUnknownEntity_returns404() throws Exception {
assertThat(generateActualJson("_MISSING-ENTITY_")).isEqualTo(
generateExpectedJson("_MISSING-ENTITY_ not found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
public void testUnknownEntity_notFound() throws Exception {
runNotFoundTest("_MISSING-ENTITY_");
}
@Test
public void testValidRegistrantContact_works() throws Exception {
assertThat(generateActualJson(registrant.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
registrant.getRepoId(), "rdap_associated_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
login("evilregistrar");
runSuccessfulTest(registrant.getRepoId(), "rdap_associated_contact.json");
}
@Test
public void testValidRegistrantContact_works_asAdministrator() throws Exception {
UserAuthInfo adminUserAuthInfo = UserAuthInfo.create(user, true);
action.authResult = AuthResult.create(AuthLevel.USER, adminUserAuthInfo);
when(sessionUtils.checkRegistrarConsoleLogin(request, adminUserAuthInfo)).thenReturn(false);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("noregistrar");
assertThat(generateActualJson(registrant.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
registrant.getRepoId(), "rdap_associated_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testValidRegistrantContact_found_sameRegistrarRequested() throws Exception {
login("evilregistrar");
action.registrarParam = Optional.of("evilregistrar");
runSuccessfulTest(registrant.getRepoId(), "rdap_associated_contact.json");
}
@Test
public void testValidRegistrantContact_works_notLoggedIn() throws Exception {
when(sessionUtils.checkRegistrarConsoleLogin(request, userAuthInfo)).thenReturn(false);
assertThat(generateActualJson(registrant.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
registrant.getRepoId(), true, "rdap_associated_contact_no_personal_data.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testValidRegistrantContact_notFound_differentRegistrarRequested() throws Exception {
login("evilregistrar");
action.registrarParam = Optional.of("idnregistrar");
runNotFoundTest(registrant.getRepoId());
}
@Test
public void testValidRegistrantContact_works_loggedInAsOtherRegistrar() throws Exception {
when(sessionUtils.getRegistrarClientId(request)).thenReturn("otherregistrar");
assertThat(generateActualJson(registrant.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
registrant.getRepoId(), true, "rdap_associated_contact_no_personal_data.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testValidRegistrantContact_found_asAdministrator() throws Exception {
loginAsAdmin();
runSuccessfulTest(registrant.getRepoId(), "rdap_associated_contact.json");
}
@Test
public void testValidRegistrantContact_found_notLoggedIn() throws Exception {
runSuccessfulTest(
registrant.getRepoId(),
"(◕‿◕)",
"active",
null,
true,
"rdap_associated_contact_no_personal_data.json");
}
@Test
public void testValidRegistrantContact_found_loggedInAsOtherRegistrar() throws Exception {
login("otherregistrar");
runSuccessfulTest(
registrant.getRepoId(),
"(◕‿◕)",
"active",
null,
true,
"rdap_associated_contact_no_personal_data.json");
}
@Test
public void testValidAdminContact_works() throws Exception {
assertThat(generateActualJson(adminContact.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
adminContact.getRepoId(), "rdap_associated_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
login("evilregistrar");
runSuccessfulTest(adminContact.getRepoId(), "rdap_associated_contact.json");
}
@Test
public void testValidTechContact_works() throws Exception {
assertThat(generateActualJson(techContact.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
techContact.getRepoId(), "rdap_associated_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
login("evilregistrar");
runSuccessfulTest(techContact.getRepoId(), "rdap_associated_contact.json");
}
@Test
public void testValidDisconnectedContact_works() throws Exception {
assertThat(generateActualJson(disconnectedContact.getRepoId())).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
disconnectedContact.getRepoId(), "rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
login("evilregistrar");
runSuccessfulTest(disconnectedContact.getRepoId(), "rdap_contact.json");
}
@Test
public void testRegistrar_works() throws Exception {
assertThat(generateActualJson("101")).isEqualTo(
generateExpectedJsonWithTopLevelEntries("101", "rdap_registrar.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testDeletedContact_notFound() throws Exception {
runNotFoundTest(deletedContact.getRepoId());
}
@Test
public void testDeletedContact_notFound_includeDeletedSetFalse() throws Exception {
action.includeDeletedParam = Optional.of(false);
runNotFoundTest(deletedContact.getRepoId());
}
@Test
public void testDeletedContact_notFound_notLoggedIn() throws Exception {
action.includeDeletedParam = Optional.of(true);
runNotFoundTest(deletedContact.getRepoId());
}
@Test
public void testDeletedContact_notFound_loggedInAsDifferentRegistrar() throws Exception {
login("idnregistrar");
action.includeDeletedParam = Optional.of(true);
runNotFoundTest(deletedContact.getRepoId());
}
@Test
public void testDeletedContact_found_loggedInAsCorrectRegistrar() throws Exception {
login("evilregistrar");
action.includeDeletedParam = Optional.of(true);
runSuccessfulTest(
deletedContact.getRepoId(),
"(◕‿◕)",
"active",
"\"2 Smiley Row\"",
false,
"rdap_contact_deleted.json");
}
@Test
public void testDeletedContact_found_loggedInAsAdmin() throws Exception {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
runSuccessfulTest(
deletedContact.getRepoId(),
"(◕‿◕)",
"active",
"\"2 Smiley Row\"",
false,
"rdap_contact_deleted.json");
}
@Test
public void testRegistrar_found() throws Exception {
runSuccessfulTest("101", "Yes Virginia <script>", "rdap_registrar.json");
}
@Test
public void testRegistrar102_works() throws Exception {
generateActualJson("102");
assertThat(response.getStatus()).isEqualTo(200);
runSuccessfulTest("102", "IDN Registrar", "rdap_registrar.json");
}
@Test
public void testRegistrar102_found_requestingSameRegistrar() throws Exception {
action.registrarParam = Optional.of("idnregistrar");
runSuccessfulTest("102", "IDN Registrar", "rdap_registrar.json");
}
@Test
public void testRegistrar102_notFound_requestingOtherRegistrar() throws Exception {
action.registrarParam = Optional.of("1tldregistrar");
runNotFoundTest("102");
}
@Test
public void testRegistrar103_works() throws Exception {
generateActualJson("103");
assertThat(response.getStatus()).isEqualTo(200);
runSuccessfulTest("103", "Multilevel Registrar", "rdap_registrar.json");
}
@Test
public void testRegistrar104_doesNotExist() throws Exception {
generateActualJson("104");
assertThat(response.getStatus()).isEqualTo(404);
public void testRegistrar104_notFound() throws Exception {
runNotFoundTest("104");
}
@Test
public void testDeletedContact_returns404() throws Exception {
assertThat(generateActualJson(deletedContact.getRepoId())).isEqualTo(
generateExpectedJson(deletedContact.getRepoId() + " not found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
public void testRegistrar104_notFound_deletedFlagWhenNotLoggedIn() throws Exception {
action.includeDeletedParam = Optional.of(true);
runNotFoundTest("104");
}
@Test
public void testRegistrar104_found_deletedFlagWhenLoggedIn() throws Exception {
login("deletedregistrar");
action.includeDeletedParam = Optional.of(true);
runSuccessfulTest(
"104", "Yes Virginia <script>", "removed", null, false, "rdap_registrar.json");
}
@Test
public void testRegistrar104_notFound_deletedFlagWhenLoggedInAsOther() throws Exception {
login("1tldregistrar");
action.includeDeletedParam = Optional.of(true);
runNotFoundTest("104");
}
@Test
public void testRegistrar104_found_deletedFlagWhenLoggedInAsAdmin() throws Exception {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
runSuccessfulTest(
"104", "Yes Virginia <script>", "removed", null, false, "rdap_registrar.json");
}
@Test
public void testRegistrar105_doesNotExist() throws Exception {
runNotFoundTest("105");
}
@Test
public void testQueryParameter_ignored() throws Exception {
login("evilregistrar");
assertThat(generateActualJson(techContact.getRepoId() + "?key=value")).isEqualTo(
generateExpectedJsonWithTopLevelEntries(
techContact.getRepoId(), "rdap_associated_contact.json"));

View file

@ -14,6 +14,7 @@
package google.registry.rdap;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
@ -32,7 +33,6 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.request.auth.AuthLevel;
@ -67,11 +67,10 @@ public class RdapEntitySearchActionTest {
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01T00:00:00Z"));
private final SessionUtils sessionUtils = mock(SessionUtils.class);
private final User user = new User("rdap.user@example.com", "gmail.com", "12345");
UserAuthInfo userAuthInfo = UserAuthInfo.create(user, false);
private final UserAuthInfo userAuthInfo = UserAuthInfo.create(user, false);
private final UserAuthInfo adminUserAuthInfo = UserAuthInfo.create(user, true);
private final RdapEntitySearchAction action = new RdapEntitySearchAction();
private ContactResource contact;
private Registrar registrarDeleted;
private Registrar registrarInactive;
private Registrar registrarTest;
@ -108,14 +107,14 @@ public class RdapEntitySearchActionTest {
// test
registrarTest =
persistResource(
makeRegistrar("2-RegistrarTest", "No Way", Registrar.State.ACTIVE)
makeRegistrar("2-RegistrarTest", "Da Test Registrar", Registrar.State.ACTIVE)
.asBuilder()
.setType(Registrar.Type.TEST)
.setIanaIdentifier(null)
.build());
persistSimpleResources(makeRegistrarContacts(registrarTest));
contact = makeAndPersistContactResource(
makeAndPersistContactResource(
"blinky",
"Blinky (赤ベイ)",
"blinky@b.tld",
@ -131,9 +130,14 @@ public class RdapEntitySearchActionTest {
clock.nowUtc(),
registrarTest);
persistResource(
makeContactResource("clyde", "Clyde (愚図た)", "clyde@c.tld", registrarDeleted)
.asBuilder().setDeletionTime(clock.nowUtc().minusDays(1)).build());
makeAndPersistContactResource(
"clyde",
"Clyde (愚図た)",
"clyde@c.tld",
ImmutableList.of("123 Example Blvd <script>"),
clock.nowUtc().minusYears(1),
registrarDeleted,
clock.nowUtc().minusMonths(6));
action.clock = clock;
action.request = request;
@ -145,10 +149,21 @@ public class RdapEntitySearchActionTest {
action.rdapWhoisServer = null;
action.fnParam = Optional.absent();
action.handleParam = Optional.absent();
action.registrarParam = Optional.absent();
action.includeDeletedParam = Optional.absent();
action.sessionUtils = sessionUtils;
action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo);
}
private void login(String registrar) {
when(sessionUtils.checkRegistrarConsoleLogin(request, userAuthInfo)).thenReturn(true);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("2-RegistrarTest");
when(sessionUtils.getRegistrarClientId(request)).thenReturn(registrar);
}
private void loginAsAdmin() {
action.authResult = AuthResult.create(AuthLevel.USER, adminUserAuthInfo);
when(sessionUtils.checkRegistrarConsoleLogin(request, adminUserAuthInfo)).thenReturn(true);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("noregistrar");
}
private Object generateExpectedJson(String expectedOutputFile) {
@ -158,13 +173,16 @@ public class RdapEntitySearchActionTest {
ImmutableMap.of("TYPE", "entity")));
}
private Object generateExpectedJson(String name, String expectedOutputFile) {
return generateExpectedJson(name, null, null, null, expectedOutputFile);
private Object generateExpectedJson(
String handle,
String expectedOutputFile) {
return generateExpectedJson(handle, null, "active", null, null, expectedOutputFile);
}
private Object generateExpectedJson(
String handle,
@Nullable String fullName,
String status,
@Nullable String email,
@Nullable String address,
String expectedOutputFile) {
@ -180,19 +198,22 @@ public class RdapEntitySearchActionTest {
builder.put("ADDRESS", address);
}
builder.put("TYPE", "entity");
return JSONValue.parse(
loadFileWithSubstitutions(this.getClass(), expectedOutputFile, builder.build()));
builder.put("STATUS", status);
String substitutedFile =
loadFileWithSubstitutions(this.getClass(), expectedOutputFile, builder.build());
Object jsonObject = JSONValue.parse(substitutedFile);
checkNotNull(jsonObject, "substituted file is not valid JSON: %s", substitutedFile);
return jsonObject;
}
private Object generateExpectedJsonForEntity(
String handle,
String fullName,
String status,
@Nullable String email,
@Nullable String address,
String expectedOutputFile) {
Object obj =
generateExpectedJson(
handle, fullName, email, address, expectedOutputFile);
Object obj = generateExpectedJson(handle, fullName, status, email, address, expectedOutputFile);
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
builder.put("entitySearchResults", ImmutableList.of(obj));
builder.put("rdapConformance", ImmutableList.of("rdap_level_0"));
@ -235,6 +256,84 @@ public class RdapEntitySearchActionTest {
assertThat(domains).hasSize(expected);
}
private void runSuccessfulNameTestWithBlinky(String queryString, String fileName) {
runSuccessfulNameTest(
queryString,
"2-ROID",
"Blinky (赤ベイ)",
"active",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
fileName);
}
private void runSuccessfulNameTest(
String queryString,
String handle,
@Nullable String fullName,
String fileName) {
runSuccessfulNameTest(queryString, handle, fullName, "active", null, null, fileName);
}
private void runSuccessfulNameTest(
String queryString,
String handle,
@Nullable String fullName,
String status,
@Nullable String email,
@Nullable String address,
String fileName) {
assertThat(generateActualJsonWithFullName(queryString))
.isEqualTo(
generateExpectedJsonForEntity(handle, fullName, status, email, address, fileName));
assertThat(response.getStatus()).isEqualTo(200);
}
private void runNotFoundNameTest(String fullName) {
assertThat(generateActualJsonWithFullName(fullName))
.isEqualTo(generateExpectedJson("No entities found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
}
private void runSuccessfulHandleTestWithBlinky(String queryString, String fileName) {
runSuccessfulHandleTest(
queryString,
"2-ROID",
"Blinky (赤ベイ)",
"active",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
fileName);
}
private void runSuccessfulHandleTest(
String queryString,
String handle,
@Nullable String fullName,
String fileName) {
runSuccessfulHandleTest(queryString, handle, fullName, "active", null, null, fileName);
}
private void runSuccessfulHandleTest(
String queryString,
String handle,
@Nullable String fullName,
String status,
@Nullable String email,
@Nullable String address,
String fileName) {
assertThat(generateActualJsonWithHandle(queryString))
.isEqualTo(
generateExpectedJsonForEntity(handle, fullName, status, email, address, fileName));
assertThat(response.getStatus()).isEqualTo(200);
}
private void runNotFoundHandleTest(String handle) {
assertThat(generateActualJsonWithHandle(handle))
.isEqualTo(generateExpectedJson("No entities found", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
public void testInvalidPath_rejected() throws Exception {
action.requestPath = RdapEntitySearchAction.PATH + "/path";
@ -280,7 +379,7 @@ public class RdapEntitySearchActionTest {
assertThat(generateActualJsonWithHandle("*"))
.isEqualTo(
generateExpectedJson(
"Initial search string required in wildcard entity handle searches",
"Initial search string must be at least 2 characters",
"rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
}
@ -290,106 +389,114 @@ public class RdapEntitySearchActionTest {
assertThat(generateActualJsonWithHandle("a*"))
.isEqualTo(
generateExpectedJson(
"Initial search string required in wildcard entity handle searches",
"Initial search string must be at least 2 characters",
"rdap_error_422.json"));
assertThat(response.getStatus()).isEqualTo(422);
}
@Test
public void testNameMatch_contactFound() throws Exception {
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_found() throws Exception {
login("2-RegistrarTest");
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
}
@Test
public void testNameMatch_contactFound_asAdministrator() throws Exception {
UserAuthInfo adminUserAuthInfo = UserAuthInfo.create(user, true);
action.authResult = AuthResult.create(AuthLevel.USER, adminUserAuthInfo);
when(sessionUtils.checkRegistrarConsoleLogin(request, adminUserAuthInfo)).thenReturn(false);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("noregistrar");
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_found_specifyingSameRegistrar() throws Exception {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarTest");
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
}
@Test
public void testNameMatch_contactFound_notLoggedIn() throws Exception {
when(sessionUtils.checkRegistrarConsoleLogin(request, userAuthInfo)).thenReturn(false);
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact_no_personal_data_with_remark.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_notFound_specifyingOtherRegistrar() throws Exception {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarInact");
runNotFoundNameTest("Blinky (赤ベイ)");
}
@Test
public void testNameMatch_contactFound_loggedInAsOtherRegistrar() throws Exception {
when(sessionUtils.getRegistrarClientId(request)).thenReturn("otherregistrar");
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact_no_personal_data_with_remark.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_found_asAdministrator() throws Exception {
loginAsAdmin();
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
}
@Test
public void testNameMatch_contactWildcardFound() throws Exception {
assertThat(generateActualJsonWithFullName("Blinky*"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_found_notLoggedIn() throws Exception {
runSuccessfulNameTestWithBlinky(
"Blinky (赤ベイ)", "rdap_contact_no_personal_data_with_remark.json");
}
@Test
public void testNameMatch_contactWildcardFoundBoth() throws Exception {
public void testNameMatchContact_found_loggedInAsOtherRegistrar() throws Exception {
login("2-Registrar");
runSuccessfulNameTestWithBlinky(
"Blinky (赤ベイ)", "rdap_contact_no_personal_data_with_remark.json");
}
@Test
public void testNameMatchContact_found_wildcard() throws Exception {
login("2-RegistrarTest");
runSuccessfulNameTestWithBlinky("Blinky*", "rdap_contact.json");
}
@Test
public void testNameMatchContact_found_wildcardBoth() throws Exception {
login("2-RegistrarTest");
assertThat(generateActualJsonWithFullName("Blin*"))
.isEqualTo(generateExpectedJson("rdap_multiple_contacts2.json"));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testNameMatch_deletedContactNotFound() throws Exception {
generateActualJsonWithFullName("Cl*");
assertThat(response.getStatus()).isEqualTo(404);
public void testNameMatchContact_notFound_deleted() throws Exception {
login("2-RegistrarTest");
runNotFoundNameTest("Cl*");
}
@Test
public void testNameMatch_registrarFound() throws Exception {
assertThat(generateActualJsonWithFullName("Yes Virginia <script>"))
.isEqualTo(
generateExpectedJsonForEntity(
"20", "Yes Virginia <script>", null, null, "rdap_registrar.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchContact_notFound_deletedWhenLoggedInAsOtherRegistrar() throws Exception {
login("2-RegistrarTest");
action.includeDeletedParam = Optional.of(true);
runNotFoundNameTest("Cl*");
}
@Test
public void testNameMatch_nonTruncatedContacts() throws Exception {
public void testNameMatchContact_found_deletedWhenLoggedInAsSameRegistrar() throws Exception {
login("2-Registrar");
action.includeDeletedParam = Optional.of(true);
runSuccessfulNameTest(
"Cl*",
"6-ROID",
"Clyde (愚図た)",
"removed",
"clyde@c.tld",
"\"123 Example Blvd <script>\"",
"rdap_contact_deleted.json");
}
@Test
public void testNameMatchRegistrar_found() throws Exception {
login("2-RegistrarTest");
runSuccessfulNameTest(
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
}
@Test
public void testNameMatchRegistrar_found_specifyingSameRegistrar() throws Exception {
action.registrarParam = Optional.of("2-Registrar");
runSuccessfulNameTest(
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
}
@Test
public void testNameMatchRegistrar_notFound_specifyingDifferentRegistrar() throws Exception {
action.registrarParam = Optional.of("2-RegistrarTest");
runNotFoundNameTest("Yes Virginia <script>");
}
@Test
public void testNameMatchContacts_nonTruncated() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(4, 0, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_nontruncated_contacts.json"));
@ -397,7 +504,8 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_truncatedContacts() throws Exception {
public void testNameMatchContacts_truncated() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(5, 0, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_truncated_contacts.json"));
@ -405,7 +513,8 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_reallyTruncatedContacts() throws Exception {
public void testNameMatchContacts_reallyTruncated() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(9, 0, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_truncated_contacts.json"));
@ -413,7 +522,7 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_nonTruncatedRegistrars() throws Exception {
public void testNameMatchRegistrars_nonTruncated() throws Exception {
createManyContactsAndRegistrars(0, 4, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_nontruncated_registrars.json"));
@ -421,7 +530,7 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_truncatedRegistrars() throws Exception {
public void testNameMatchRegistrars_truncated() throws Exception {
createManyContactsAndRegistrars(0, 5, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_truncated_registrars.json"));
@ -429,7 +538,7 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_reallyTruncatedRegistrars() throws Exception {
public void testNameMatchRegistrars_reallyTruncated() throws Exception {
createManyContactsAndRegistrars(0, 9, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_truncated_registrars.json"));
@ -437,7 +546,8 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testNameMatch_truncatedMixOfContactsAndRegistrars() throws Exception {
public void testNameMatchMix_truncated() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(3, 3, registrarTest);
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_truncated_mixed_entities.json"));
@ -445,89 +555,148 @@ public class RdapEntitySearchActionTest {
}
@Test
public void testHandleMatch_2roid_found() throws Exception {
assertThat(generateActualJsonWithHandle("2-ROID"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchRegistrar_notFound_inactive() throws Exception {
runNotFoundNameTest("No Way");
}
@Test
public void testHandleMatch_20_found() throws Exception {
assertThat(generateActualJsonWithHandle("20"))
.isEqualTo(
generateExpectedJsonForEntity(
"20", "Yes Virginia <script>", null, null, "rdap_registrar.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testNameMatchRegistrar_notFound_inactiveAsDifferentRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
runNotFoundNameTest("No Way");
}
@Test
public void testNameMatch_2registrarInactive_notFound() throws Exception {
generateActualJsonWithHandle("2-RegistrarInact");
assertThat(response.getStatus()).isEqualTo(404);
public void testNameMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact");
runSuccessfulNameTest("No Way", "21", "No Way", "removed", null, null, "rdap_registrar.json");
}
@Test
public void testNameMatch_2registrarTest_notFound() throws Exception {
generateActualJsonWithHandle("2-RegistrarTest");
assertThat(response.getStatus()).isEqualTo(404);
public void testNameMatchRegistrar_found_inactiveAsAdmin() throws Exception {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
runSuccessfulNameTest("No Way", "21", "No Way", "removed", null, null, "rdap_registrar.json");
}
@Test
public void testNameMatch_testAndInactiveRegistrars_notFound() throws Exception {
generateActualJsonWithHandle("No Way");
assertThat(response.getStatus()).isEqualTo(404);
public void testNameMatchRegistrar_notFound_test() throws Exception {
runNotFoundNameTest("Da Test Registrar");
}
@Test
public void testHandleMatch_2rstarWithResultSetSize1_foundOne() throws Exception {
public void testNameMatchRegistrar_notFound_testAsDifferentRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
runNotFoundNameTest("Da Test Registrar");
}
@Test
public void testNameMatchRegistrar_found_testAsSameRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarTest");
runSuccessfulNameTest(
"Da Test Registrar", "(none)", "Da Test Registrar", "rdap_registrar_test.json");
}
@Test
public void testNameMatchRegistrar_found_testAsAdmin() throws Exception {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
runSuccessfulNameTest(
"Da Test Registrar", "(none)", "Da Test Registrar", "rdap_registrar_test.json");
}
@Test
public void testHandleMatchContact_found() throws Exception {
login("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
}
@Test
public void testHandleMatchContact_found_specifyingSameRegistrar() throws Exception {
action.registrarParam = Optional.of("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact_no_personal_data_with_remark.json");
}
@Test
public void testHandleMatchContact_notFound_specifyingDifferentRegistrar() throws Exception {
action.registrarParam = Optional.of("2-Registrar");
runNotFoundHandleTest("2-ROID");
}
@Test
public void testHandleMatchRegistrar_found() throws Exception {
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
}
@Test
public void testHandleMatchRegistrar_found_specifyingSameRegistrar() throws Exception {
action.registrarParam = Optional.of("2-Registrar");
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
}
@Test
public void testHandleMatchRegistrar_notFound_specifyingDifferentRegistrar() throws Exception {
action.registrarParam = Optional.of("2-RegistrarTest");
runNotFoundHandleTest("20");
}
@Test
public void testHandleMatchContact_found_wildcardWithResultSetSizeOne() throws Exception {
login("2-RegistrarTest");
action.rdapResultSetMaxSize = 1;
assertThat(generateActualJsonWithHandle("2-R*"))
.isEqualTo(
generateExpectedJsonForEntity(
"2-ROID",
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
runSuccessfulHandleTestWithBlinky("2-R*", "rdap_contact.json");
}
@Test
public void testHandleMatch_2rostar_found() throws Exception {
assertThat(generateActualJsonWithHandle("2-RO*"))
.isEqualTo(
generateExpectedJsonForEntity(
contact.getRepoId(),
"Blinky (赤ベイ)",
"blinky@b.tld",
"\"123 Blinky St\", \"Blinkyland\"",
"rdap_contact.json"));
assertThat(response.getStatus()).isEqualTo(200);
public void testHandleMatchContact_found_wildcard() throws Exception {
login("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
}
@Test
public void testHandleMatch_20star_notFound() throws Exception {
generateActualJsonWithHandle("20*");
assertThat(response.getStatus()).isEqualTo(404);
public void testHandleMatchContact_notFound_wildcard() throws Exception {
runNotFoundHandleTest("20*");
}
@Test
public void testHandleMatch_3teststar_notFound() throws Exception {
generateActualJsonWithHandle("3test*");
assertThat(response.getStatus()).isEqualTo(404);
public void testHandleMatchRegistrar_notFound_wildcard() throws Exception {
runNotFoundHandleTest("3test*");
}
@Test
public void testHandleMatch_truncatedEntities() throws Exception {
public void testHandleMatchMix_found_truncated() throws Exception {
createManyContactsAndRegistrars(300, 0, registrarTest);
Object obj = generateActualJsonWithHandle("10*");
assertThat(response.getStatus()).isEqualTo(200);
checkNumberOfEntitiesInResult(obj, 4);
}
@Test
public void testHandleMatchRegistrar_notFound_inactive() throws Exception {
runNotFoundHandleTest("21");
}
@Test
public void testHandleMatchRegistrar_notFound_inactiveAsDifferentRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
runNotFoundHandleTest("21");
}
@Test
public void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() throws Exception {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact");
runSuccessfulHandleTest("21", "21", "No Way", "removed", null, null, "rdap_registrar.json");
}
@Test
public void testHandleMatchRegistrar_found_inactiveAsAdmin() throws Exception {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
runSuccessfulHandleTest("21", "21", "No Way", "removed", null, null, "rdap_registrar.json");
}
}

View file

@ -1,7 +1,7 @@
{
"objectClassName" : "entity",
"handle" : "%NAME%",
"status" : ["active"],
"status" : ["%STATUS%"],
"links" :
[
{
@ -46,4 +46,3 @@
]
]
}

View file

@ -0,0 +1,54 @@
{
"objectClassName" : "entity",
"handle" : "%NAME%",
"status" : ["removed"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/%NAME%",
"rel" : "self",
"href": "https://example.com/rdap/entity/%NAME%",
"type" : "application/rdap+json"
}
],
"events":
[
{
"eventAction": "registration",
"eventActor": "foo",
"eventDate": "1999-01-01T00:00:00.000Z"
},
{
"eventAction": "deletion",
"eventActor": "foo",
"eventDate": "1999-07-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "%FULLNAME%"],
["org", {}, "text", "GOOGLE INCORPORATED <script>"],
["adr", {}, "text",
[
"",
"",
%ADDRESS%,
"KOKOMO",
"BM",
"31337",
"United States"
]
],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2126660420"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2126660420"],
["email", {}, "text", "%EMAIL%"]
]
]
}

View file

@ -3,14 +3,14 @@
[
{
"objectClassName" : "entity",
"handle" : "7-ROID",
"handle" : "9-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/7-ROID",
"value" : "https://example.com/rdap/entity/9-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/7-ROID",
"href": "https://example.com/rdap/entity/9-ROID",
"type" : "application/rdap+json"
}
],
@ -49,14 +49,14 @@
},
{
"objectClassName" : "entity",
"handle" : "8-ROID",
"handle" : "A-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/8-ROID",
"value" : "https://example.com/rdap/entity/A-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/8-ROID",
"href": "https://example.com/rdap/entity/A-ROID",
"type" : "application/rdap+json"
}
],
@ -95,14 +95,14 @@
},
{
"objectClassName" : "entity",
"handle" : "9-ROID",
"handle" : "B-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/9-ROID",
"value" : "https://example.com/rdap/entity/B-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/9-ROID",
"href": "https://example.com/rdap/entity/B-ROID",
"type" : "application/rdap+json"
}
],
@ -141,14 +141,14 @@
},
{
"objectClassName" : "entity",
"handle" : "A-ROID",
"handle" : "C-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/A-ROID",
"value" : "https://example.com/rdap/entity/C-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/A-ROID",
"href": "https://example.com/rdap/entity/C-ROID",
"type" : "application/rdap+json"
}
],

View file

@ -1,7 +1,7 @@
{
"objectClassName" : "entity",
"handle" : "%NAME%",
"status" : ["active"],
"status" : ["%STATUS%"],
"roles" : ["registrar"],
"links" :
[
@ -35,7 +35,7 @@
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "Yes Virginia <script>"],
["fn", {}, "text", "%FULLNAME%"],
["adr", {}, "text",
[
"",

View file

@ -0,0 +1,74 @@
{
"objectClassName" : "entity",
"handle" : "%NAME%",
"status" : ["%STATUS%"],
"roles" : ["registrar"],
"events": [
{
"eventAction": "registration",
"eventActor": "%NAME%",
"eventDate": "2000-01-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "%FULLNAME%"],
["adr", {}, "text",
[
"",
"",
"123 Example Boulevard <script>"
"Williamsburg <script>",
"NY",
"11211",
"United States"
]
],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2125551212"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2125551213"],
["email", {}, "text", "contact-us@example.com"]
]
],
"entities" :
[
{
"objectClassName" : "entity",
"status" : ["active"],
"roles" : ["administrative"],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "Jane Doe"],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2125551215"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2125551216"],
["email", {}, "text", "janedoe@example.com"]
]
],
},
{
"objectClassName" : "entity",
"status" : ["active"],
"roles" : ["technical"],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "John Doe"],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2125551213"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2125551213"],
["email", {}, "text", "johndoe@example.com"]
]
],
}
]
}

View file

@ -3,14 +3,14 @@
[
{
"objectClassName" : "entity",
"handle" : "7-ROID",
"handle" : "9-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/7-ROID",
"value" : "https://example.com/rdap/entity/9-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/7-ROID",
"href": "https://example.com/rdap/entity/9-ROID",
"type" : "application/rdap+json"
}
],
@ -49,14 +49,14 @@
},
{
"objectClassName" : "entity",
"handle" : "8-ROID",
"handle" : "A-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/8-ROID",
"value" : "https://example.com/rdap/entity/A-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/8-ROID",
"href": "https://example.com/rdap/entity/A-ROID",
"type" : "application/rdap+json"
}
],
@ -95,14 +95,14 @@
},
{
"objectClassName" : "entity",
"handle" : "9-ROID",
"handle" : "B-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/9-ROID",
"value" : "https://example.com/rdap/entity/B-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/9-ROID",
"href": "https://example.com/rdap/entity/B-ROID",
"type" : "application/rdap+json"
}
],
@ -141,14 +141,14 @@
},
{
"objectClassName" : "entity",
"handle" : "A-ROID",
"handle" : "C-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/A-ROID",
"value" : "https://example.com/rdap/entity/C-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/A-ROID",
"href": "https://example.com/rdap/entity/C-ROID",
"type" : "application/rdap+json"
}
],

View file

@ -3,14 +3,14 @@
[
{
"objectClassName" : "entity",
"handle" : "7-ROID",
"handle" : "9-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/7-ROID",
"value" : "https://example.com/rdap/entity/9-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/7-ROID",
"href": "https://example.com/rdap/entity/9-ROID",
"type" : "application/rdap+json"
}
],
@ -49,14 +49,14 @@
},
{
"objectClassName" : "entity",
"handle" : "8-ROID",
"handle" : "A-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/8-ROID",
"value" : "https://example.com/rdap/entity/A-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/8-ROID",
"href": "https://example.com/rdap/entity/A-ROID",
"type" : "application/rdap+json"
}
],
@ -95,14 +95,14 @@
},
{
"objectClassName" : "entity",
"handle" : "9-ROID",
"handle" : "B-ROID",
"status" : ["active"],
"links" :
[
{
"value" : "https://example.com/rdap/entity/9-ROID",
"value" : "https://example.com/rdap/entity/B-ROID",
"rel" : "self",
"href": "https://example.com/rdap/entity/9-ROID",
"href": "https://example.com/rdap/entity/B-ROID",
"type" : "application/rdap+json"
}
],

View file

@ -178,6 +178,16 @@ public final class FullFieldsTestEntityHelper {
@Nullable String email,
@Nullable List<String> street,
@Nullable Registrar registrar) {
return makeContactResource(id, name, email, street, registrar, null);
}
public static ContactResource makeContactResource(
String id,
String name,
@Nullable String email,
@Nullable List<String> street,
@Nullable Registrar registrar,
@Nullable DateTime deletionTime) {
PostalInfo.Builder postalBuilder = new PostalInfo.Builder()
.setType(PostalInfo.Type.INTERNATIONALIZED)
.setName(name)
@ -212,6 +222,9 @@ public final class FullFieldsTestEntityHelper {
.setCreationClientId(registrar.getClientId())
.setPersistedCurrentSponsorClientId(registrar.getClientId());
}
if (deletionTime != null) {
builder.setDeletionTime(deletionTime);
}
return builder.build();
}
@ -227,7 +240,8 @@ public final class FullFieldsTestEntityHelper {
email,
ImmutableList.of("123 Example Boulevard <script>"),
creationTime,
registrar);
registrar,
null);
}
public static ContactResource makeAndPersistContactResource(
@ -236,7 +250,7 @@ public final class FullFieldsTestEntityHelper {
@Nullable String email,
@Nullable List<String> street,
@Nullable DateTime creationTime) {
return makeAndPersistContactResource(id, name, email, street, creationTime, null);
return makeAndPersistContactResource(id, name, email, street, creationTime, null, null);
}
public static ContactResource makeAndPersistContactResource(
@ -246,12 +260,27 @@ public final class FullFieldsTestEntityHelper {
@Nullable List<String> street,
@Nullable DateTime creationTime,
@Nullable Registrar registrar) {
return makeAndPersistContactResource(id, name, email, street, creationTime, registrar, null);
}
public static ContactResource makeAndPersistContactResource(
String id,
String name,
@Nullable String email,
@Nullable List<String> street,
@Nullable DateTime creationTime,
@Nullable Registrar registrar,
@Nullable DateTime deletionTime) {
ContactResource contactResource =
persistResource(makeContactResource(id, name, email, street, registrar));
persistResource(makeContactResource(id, name, email, street, registrar, deletionTime));
if (creationTime != null) {
persistResource(makeHistoryEntry(
contactResource, HistoryEntry.Type.CONTACT_CREATE, null, "created", creationTime));
}
if (deletionTime != null) {
persistResource(makeHistoryEntry(
contactResource, HistoryEntry.Type.CONTACT_DELETE, null, "deleted", deletionTime));
}
return contactResource;
}