Skip to content

Commit 8e41278

Browse files
authored
Include GP statuses in RDAP results (#2606)
We do this for WHOIS results so we should do it for RDAP results as well (especially since they're mostly already included in the response profile).
1 parent cb3738d commit 8e41278

11 files changed

+987
-56
lines changed

core/src/main/java/google/registry/rdap/RdapJsonFormatter.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,21 @@
3131
import com.google.common.collect.ImmutableSetMultimap;
3232
import com.google.common.collect.Maps;
3333
import com.google.common.collect.Ordering;
34+
import com.google.common.collect.Sets;
3435
import com.google.common.collect.Streams;
3536
import com.google.common.flogger.FluentLogger;
3637
import com.google.common.net.InetAddresses;
3738
import com.google.gson.JsonArray;
3839
import google.registry.config.RegistryConfig.Config;
3940
import google.registry.model.EppResource;
41+
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
4042
import google.registry.model.contact.Contact;
4143
import google.registry.model.contact.ContactPhoneNumber;
4244
import google.registry.model.contact.PostalInfo;
4345
import google.registry.model.domain.DesignatedContact;
4446
import google.registry.model.domain.DesignatedContact.Type;
4547
import google.registry.model.domain.Domain;
48+
import google.registry.model.domain.rgp.GracePeriodStatus;
4649
import google.registry.model.eppcommon.Address;
4750
import google.registry.model.eppcommon.StatusValue;
4851
import google.registry.model.host.Host;
@@ -175,10 +178,8 @@ public enum OutputDataType {
175178
+ " repoId = '%repoIdValue%' and type is not null group by type)";
176179

177180
/** Map of EPP status values to the RDAP equivalents. */
178-
private static final ImmutableMap<StatusValue, RdapStatus> STATUS_TO_RDAP_STATUS_MAP =
179-
new ImmutableMap.Builder<StatusValue, RdapStatus>()
180-
// RdapStatus.ADD_PERIOD not defined in our system
181-
// RdapStatus.AUTO_RENEW_PERIOD not defined in our system
181+
private static final ImmutableMap<EppEnum, RdapStatus> STATUS_TO_RDAP_STATUS_MAP =
182+
new ImmutableMap.Builder<EppEnum, RdapStatus>()
182183
.put(StatusValue.CLIENT_DELETE_PROHIBITED, RdapStatus.CLIENT_DELETE_PROHIBITED)
183184
.put(StatusValue.CLIENT_HOLD, RdapStatus.CLIENT_HOLD)
184185
.put(StatusValue.CLIENT_RENEW_PROHIBITED, RdapStatus.CLIENT_RENEW_PROHIBITED)
@@ -190,17 +191,21 @@ public enum OutputDataType {
190191
.put(StatusValue.PENDING_CREATE, RdapStatus.PENDING_CREATE)
191192
.put(StatusValue.PENDING_DELETE, RdapStatus.PENDING_DELETE)
192193
// RdapStatus.PENDING_RENEW not defined in our system
193-
// RdapStatus.PENDING_RESTORE not defined in our system
194194
.put(StatusValue.PENDING_TRANSFER, RdapStatus.PENDING_TRANSFER)
195195
.put(StatusValue.PENDING_UPDATE, RdapStatus.PENDING_UPDATE)
196-
// RdapStatus.REDEMPTION_PERIOD not defined in our system
197-
// RdapStatus.RENEW_PERIOD not defined in our system
198196
.put(StatusValue.SERVER_DELETE_PROHIBITED, RdapStatus.SERVER_DELETE_PROHIBITED)
199197
.put(StatusValue.SERVER_HOLD, RdapStatus.SERVER_HOLD)
200198
.put(StatusValue.SERVER_RENEW_PROHIBITED, RdapStatus.SERVER_RENEW_PROHIBITED)
201199
.put(StatusValue.SERVER_TRANSFER_PROHIBITED, RdapStatus.SERVER_TRANSFER_PROHIBITED)
202200
.put(StatusValue.SERVER_UPDATE_PROHIBITED, RdapStatus.SERVER_UPDATE_PROHIBITED)
203-
// RdapStatus.TRANSFER_PERIOD not defined in our system
201+
.put(GracePeriodStatus.ADD, RdapStatus.ADD_PERIOD)
202+
.put(GracePeriodStatus.AUTO_RENEW, RdapStatus.AUTO_RENEW_PERIOD)
203+
.put(GracePeriodStatus.REDEMPTION, RdapStatus.REDEMPTION_PERIOD)
204+
.put(GracePeriodStatus.RENEW, RdapStatus.RENEW_PERIOD)
205+
.put(GracePeriodStatus.PENDING_DELETE, RdapStatus.PENDING_DELETE)
206+
// In practice, PENDING_RESTORE is unused. We just perform the restore immediately
207+
.put(GracePeriodStatus.PENDING_RESTORE, RdapStatus.PENDING_RESTORE)
208+
.put(GracePeriodStatus.TRANSFER, RdapStatus.TRANSFER_PERIOD)
204209
.build();
205210

206211
/**
@@ -348,9 +353,11 @@ RdapDomain createRdapDomain(Domain domain, OutputDataType outputDataType) {
348353
}
349354
// RDAP Response Profile 2.6.1: must have at least one status member
350355
// makeStatusValueList should in theory always contain one of either "active" or "inactive".
356+
Set<EppEnum> allStatusValues =
357+
Sets.union(domain.getStatusValues(), domain.getGracePeriodStatuses());
351358
ImmutableSet<RdapStatus> status =
352359
makeStatusValueList(
353-
domain.getStatusValues(),
360+
allStatusValues,
354361
false, // isRedacted
355362
domain.getDeletionTime().isBefore(getRequestTime()));
356363
builder.statusBuilder().addAll(status);
@@ -1045,7 +1052,7 @@ private static String makePhoneString(ContactPhoneNumber phoneNumber) {
10451052
* redacted objects.
10461053
*/
10471054
private static ImmutableSet<RdapStatus> makeStatusValueList(
1048-
ImmutableSet<StatusValue> statusValues, boolean isRedacted, boolean isDeleted) {
1055+
Set<? extends EppEnum> statusValues, boolean isRedacted, boolean isDeleted) {
10491056
Stream<RdapStatus> stream =
10501057
statusValues.stream()
10511058
.map(status -> STATUS_TO_RDAP_STATUS_MAP.getOrDefault(status, RdapStatus.OBSCURED));

core/src/main/java/google/registry/rdap/RdapObjectClasses.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ final class RdapObjectClasses {
5151
*/
5252
@RestrictJsonNames({})
5353
@AutoValue
54-
abstract static class Vcard implements Jsonable {
54+
public abstract static class Vcard implements Jsonable {
5555
abstract String property();
5656
abstract ImmutableMap<String, ImmutableList<String>> parameters();
5757
abstract String valueType();
@@ -94,7 +94,7 @@ public JsonArray toJson() {
9494

9595
@RestrictJsonNames("vcardArray")
9696
@AutoValue
97-
abstract static class VcardArray implements Jsonable {
97+
public abstract static class VcardArray implements Jsonable {
9898

9999
private static final String VCARD_VERSION_NUMBER = "4.0";
100100
private static final Vcard VCARD_ENTRY_VERSION =
@@ -148,7 +148,7 @@ public enum BoilerplateType {
148148
*
149149
* <p>All Actions need to return an object of this type.
150150
*/
151-
abstract static class ReplyPayloadBase extends AbstractJsonableObject {
151+
public abstract static class ReplyPayloadBase extends AbstractJsonableObject {
152152
final BoilerplateType boilerplateType;
153153

154154
ReplyPayloadBase(BoilerplateType boilerplateType) {
@@ -165,7 +165,7 @@ abstract static class ReplyPayloadBase extends AbstractJsonableObject {
165165
*/
166166
@AutoValue
167167
@RestrictJsonNames({})
168-
abstract static class TopLevelReplyObject extends AbstractJsonableObject {
168+
public abstract static class TopLevelReplyObject extends AbstractJsonableObject {
169169
@JsonableElement("rdapConformance")
170170
static final RdapConformance RDAP_CONFORMANCE = RdapConformance.INSTANCE;
171171

@@ -257,7 +257,7 @@ abstract static class Builder<B extends Builder<?>> {
257257
* <p>We're missing the "autnums" and "networks" fields
258258
*/
259259
@RestrictJsonNames({"entities[]", "entitySearchResults[]"})
260-
abstract static class RdapEntity extends RdapObjectBase {
260+
public abstract static class RdapEntity extends RdapObjectBase {
261261

262262
/** Role values specified in RFC 9083 § 10.2.4. */
263263
@RestrictJsonNames("roles[]")
@@ -311,7 +311,7 @@ private abstract static class Builder<B extends Builder<B>> extends RdapObjectBa
311311
* for each one for type safety.
312312
*/
313313
@AutoValue
314-
abstract static class RdapRegistrarEntity extends RdapEntity {
314+
public abstract static class RdapRegistrarEntity extends RdapEntity {
315315

316316
static Builder builder() {
317317
return new AutoValue_RdapObjectClasses_RdapRegistrarEntity.Builder();
@@ -330,7 +330,7 @@ abstract static class Builder extends RdapEntity.Builder<Builder> {
330330
* for each one for type safety.
331331
*/
332332
@AutoValue
333-
abstract static class RdapContactEntity extends RdapEntity {
333+
public abstract static class RdapContactEntity extends RdapEntity {
334334

335335
static Builder builder() {
336336
return new AutoValue_RdapObjectClasses_RdapContactEntity.Builder();
@@ -387,7 +387,7 @@ abstract static class Builder<B extends Builder<?>> extends RdapObjectBase.Build
387387
/** The Nameserver Object Class defined in 5.2 of RFC 9083. */
388388
@RestrictJsonNames({"nameservers[]", "nameserverSearchResults[]"})
389389
@AutoValue
390-
abstract static class RdapNameserver extends RdapNamedObjectBase {
390+
public abstract static class RdapNameserver extends RdapNamedObjectBase {
391391

392392
@JsonableElement Optional<IpAddresses> ipAddresses() {
393393
if (ipv6().isEmpty() && ipv4().isEmpty()) {
@@ -429,7 +429,7 @@ abstract static class Builder extends RdapNamedObjectBase.Builder<Builder> {
429429
/** Object defined in RFC 9083 section 5.3, only used for RdapDomain. */
430430
@RestrictJsonNames("secureDNS")
431431
@AutoValue
432-
abstract static class SecureDns extends AbstractJsonableObject {
432+
public abstract static class SecureDns extends AbstractJsonableObject {
433433
@RestrictJsonNames("dsData[]")
434434
@AutoValue
435435
abstract static class DsData extends AbstractJsonableObject {
@@ -507,7 +507,7 @@ Builder addDsData(DomainDsData dsData) {
507507
*/
508508
@RestrictJsonNames("domainSearchResults[]")
509509
@AutoValue
510-
abstract static class RdapDomain extends RdapNamedObjectBase {
510+
public abstract static class RdapDomain extends RdapNamedObjectBase {
511511

512512
@JsonableElement abstract ImmutableList<RdapNameserver> nameservers();
513513

@@ -535,7 +535,7 @@ abstract static class Builder extends RdapNamedObjectBase.Builder<Builder> {
535535
/** Error Response Body defined in 6 of RFC 9083. */
536536
@RestrictJsonNames({})
537537
@AutoValue
538-
abstract static class ErrorResponse extends ReplyPayloadBase {
538+
public abstract static class ErrorResponse extends ReplyPayloadBase {
539539

540540
@JsonableElement final LanguageIdentifier lang = LanguageIdentifier.EN;
541541

@@ -561,7 +561,7 @@ static ErrorResponse create(int status, String title, String description) {
561561
*/
562562
@RestrictJsonNames({})
563563
@AutoValue
564-
abstract static class HelpResponse extends ReplyPayloadBase {
564+
public abstract static class HelpResponse extends ReplyPayloadBase {
565565
@JsonableElement("notices[]") abstract Optional<Notice> helpNotice();
566566

567567
HelpResponse() {

core/src/test/java/google/registry/rdap/RdapActionBaseTestCase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
/** Common unit test code for actions inheriting {@link RdapActionBase}. */
4343
abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
4444

45+
protected final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
46+
4547
@RegisterExtension
4648
final JpaIntegrationTestExtension jpa =
47-
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
49+
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
4850

4951
protected static final AuthResult AUTH_RESULT =
5052
AuthResult.createUser(
@@ -61,7 +63,6 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
6163
.build());
6264

6365
protected FakeResponse response = new FakeResponse();
64-
protected final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
6566
final RdapMetrics rdapMetrics = mock(RdapMetrics.class);
6667

6768
RdapAuthorization.Role metricRole = PUBLIC;

core/src/test/java/google/registry/rdap/RdapDomainActionTest.java

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import static com.google.common.truth.Truth.assertThat;
1818
import static google.registry.model.EppResourceUtils.loadByForeignKey;
1919
import static google.registry.testing.DatabaseHelper.createTld;
20+
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
21+
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
2022
import static google.registry.testing.DatabaseHelper.persistResource;
2123
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
2224
import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistHost;
@@ -31,7 +33,10 @@
3133
import com.google.gson.JsonObject;
3234
import google.registry.model.contact.Contact;
3335
import google.registry.model.domain.Domain;
36+
import google.registry.model.domain.GracePeriod;
3437
import google.registry.model.domain.Period;
38+
import google.registry.model.domain.rgp.GracePeriodStatus;
39+
import google.registry.model.eppcommon.StatusValue;
3540
import google.registry.model.host.Host;
3641
import google.registry.model.registrar.Registrar;
3742
import google.registry.model.reporting.HistoryEntry;
@@ -43,6 +48,7 @@
4348
import google.registry.request.Action;
4449
import google.registry.testing.FullFieldsTestEntityHelper;
4550
import java.util.Optional;
51+
import org.joda.time.DateTime;
4652
import org.junit.jupiter.api.BeforeEach;
4753
import org.junit.jupiter.api.Disabled;
4854
import org.junit.jupiter.api.Test;
@@ -59,14 +65,17 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
5965
super(RdapDomainAction.class);
6066
}
6167

68+
private Contact registrantLol;
69+
private Host host1;
70+
6271
@BeforeEach
6372
void beforeEach() {
6473
// lol
6574
createTld("lol");
6675
Registrar registrarLol = persistResource(makeRegistrar(
6776
"evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE));
6877
persistSimpleResources(makeRegistrarPocs(registrarLol));
69-
Contact registrantLol =
78+
registrantLol =
7079
FullFieldsTestEntityHelper.makeAndPersistContact(
7180
"5372808-ERL",
7281
"Goblin Market",
@@ -83,7 +92,7 @@ void beforeEach() {
8392
Contact techContactLol =
8493
FullFieldsTestEntityHelper.makeAndPersistContact(
8594
"5372808-TRL", "The Raven", "[email protected]", clock.nowUtc().minusYears(3), registrarLol);
86-
Host host1 = makeAndPersistHost("ns1.cat.lol", "1.2.3.4", null, clock.nowUtc().minusYears(1));
95+
host1 = makeAndPersistHost("ns1.cat.lol", "1.2.3.4", null, clock.nowUtc().minusYears(1));
8796
Host host2 =
8897
makeAndPersistHost(
8998
"ns2.cat.lol", "bad:f00d:cafe:0:0:0:15:beef", clock.nowUtc().minusYears(2));
@@ -493,6 +502,93 @@ void testDeletedDomain_works_loggedInAsAdmin() {
493502
assertThat(response.getStatus()).isEqualTo(200);
494503
}
495504

505+
@Test
506+
void testAddGracePeriod() {
507+
persistActiveDomainWithHost(
508+
"addgraceperiod", "lol", clock.nowUtc(), clock.nowUtc().plusYears(1));
509+
assertAboutJson()
510+
.that(generateActualJson("addgraceperiod.lol"))
511+
.isEqualTo(addBoilerplate(jsonFileBuilder().load("rdap_domain_add_grace_period.json")));
512+
}
513+
514+
@Test
515+
void testAutoRenewGracePeriod() {
516+
persistActiveDomainWithHost(
517+
"autorenew", "lol", clock.nowUtc().minusYears(1).minusDays(1), clock.nowUtc().minusDays(1));
518+
assertAboutJson()
519+
.that(generateActualJson("autorenew.lol"))
520+
.isEqualTo(
521+
addBoilerplate(jsonFileBuilder().load("rdap_domain_auto_renew_grace_period.json")));
522+
}
523+
524+
@Test
525+
void testRedemptionGracePeriod() {
526+
Domain domain = persistActiveDomain("redemption.lol", clock.nowUtc().minusYears(1));
527+
persistResource(
528+
domain
529+
.asBuilder()
530+
.addNameserver(host1.createVKey())
531+
.setDeletionTime(clock.nowUtc().plusDays(1))
532+
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
533+
.setGracePeriods(
534+
ImmutableSet.of(
535+
GracePeriod.createWithoutBillingEvent(
536+
GracePeriodStatus.REDEMPTION,
537+
domain.getRepoId(),
538+
clock.nowUtc().plusDays(4),
539+
"TheRegistrar")))
540+
.build());
541+
assertAboutJson()
542+
.that(generateActualJson("redemption.lol"))
543+
.isEqualTo(
544+
addBoilerplate(
545+
jsonFileBuilder().load("rdap_domain_pending_delete_redemption_grace_period.json")));
546+
}
547+
548+
@Test
549+
void testRenewGracePeriod() {
550+
Domain domain =
551+
persistActiveDomainWithHost(
552+
"renew", "lol", clock.nowUtc().minusYears(1), clock.nowUtc().plusYears(1));
553+
persistResource(
554+
domain
555+
.asBuilder()
556+
.addGracePeriod(
557+
GracePeriod.create(
558+
GracePeriodStatus.RENEW,
559+
domain.getRepoId(),
560+
clock.nowUtc().plusDays(1),
561+
"TheRegistrar",
562+
null))
563+
.build());
564+
assertAboutJson()
565+
.that(generateActualJson("renew.lol"))
566+
.isEqualTo(
567+
addBoilerplate(jsonFileBuilder().load("rdap_domain_explicit_renew_grace_period.json")));
568+
}
569+
570+
@Test
571+
void testTransferGracePeriod() {
572+
Domain domain =
573+
persistActiveDomainWithHost(
574+
"transfer", "lol", clock.nowUtc().minusMonths(6), clock.nowUtc().plusYears(1));
575+
persistResource(
576+
domain
577+
.asBuilder()
578+
.addGracePeriod(
579+
GracePeriod.create(
580+
GracePeriodStatus.TRANSFER,
581+
domain.getRepoId(),
582+
clock.nowUtc().plusDays(1),
583+
"TheRegistrar",
584+
null))
585+
.build());
586+
assertAboutJson()
587+
.that(generateActualJson("transfer.lol"))
588+
.isEqualTo(
589+
addBoilerplate(jsonFileBuilder().load("rdap_domain_transfer_grace_period.json")));
590+
}
591+
496592
@Test
497593
void testMetrics() {
498594
generateActualJson("cat.lol");
@@ -511,4 +607,14 @@ void testMetrics() {
511607
.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE)
512608
.build());
513609
}
610+
611+
private Domain persistActiveDomainWithHost(
612+
String label, String tld, DateTime creationTime, DateTime expirationTime) {
613+
return persistResource(
614+
persistDomainWithDependentResources(
615+
label, tld, registrantLol, clock.nowUtc(), creationTime, expirationTime)
616+
.asBuilder()
617+
.addNameserver(host1.createVKey())
618+
.build());
619+
}
514620
}

0 commit comments

Comments
 (0)