mirror of
https://github.com/google/nomulus.git
synced 2025-05-09 08:18:21 +02:00
Currently we try to reimplemnet the same behavior of the existing code as much as possible. We only fix issues that go against the RFC7483, but we don't yet update the code to follow the latest (15feb19) RDAP Response Profile. That will require a much bigger change especially for the test files, so it'll wait for a followup CL. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=246948018
442 lines
14 KiB
Java
442 lines
14 KiB
Java
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package google.registry.rdap;
|
|
|
|
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
|
|
|
|
import com.google.auto.value.AutoValue;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.Ordering;
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonPrimitive;
|
|
import google.registry.rdap.AbstractJsonableObject.RestrictJsonNames;
|
|
import google.registry.rdap.RdapDataStructures.Event;
|
|
import google.registry.rdap.RdapDataStructures.EventWithoutActor;
|
|
import google.registry.rdap.RdapDataStructures.LanguageIdentifier;
|
|
import google.registry.rdap.RdapDataStructures.Link;
|
|
import google.registry.rdap.RdapDataStructures.Notice;
|
|
import google.registry.rdap.RdapDataStructures.ObjectClassName;
|
|
import google.registry.rdap.RdapDataStructures.Port43WhoisServer;
|
|
import google.registry.rdap.RdapDataStructures.PublicId;
|
|
import google.registry.rdap.RdapDataStructures.RdapConformance;
|
|
import google.registry.rdap.RdapDataStructures.RdapStatus;
|
|
import google.registry.rdap.RdapDataStructures.Remark;
|
|
import google.registry.util.Idn;
|
|
import java.util.Optional;
|
|
|
|
/**
|
|
* Object Classes defined in RFC7483 section 5.
|
|
*/
|
|
final class RdapObjectClasses {
|
|
|
|
/**
|
|
* Temporary implementation of VCards.
|
|
*
|
|
* Will create a better implementation soon.
|
|
*/
|
|
@RestrictJsonNames({})
|
|
@AutoValue
|
|
abstract static class Vcard implements Jsonable {
|
|
abstract String property();
|
|
abstract ImmutableMap<String, ImmutableList<String>> parameters();
|
|
abstract String valueType();
|
|
abstract JsonElement value();
|
|
|
|
static Vcard create(
|
|
String property,
|
|
ImmutableMap<String, ImmutableList<String>> parameters,
|
|
String valueType,
|
|
JsonElement value) {
|
|
return new AutoValue_RdapObjectClasses_Vcard(property, parameters, valueType, value);
|
|
}
|
|
|
|
static Vcard create(
|
|
String property,
|
|
ImmutableMap<String, ImmutableList<String>> parameters,
|
|
String valueType,
|
|
String value) {
|
|
return create(property, parameters, valueType, new JsonPrimitive(value));
|
|
}
|
|
|
|
static Vcard create(String property, String valueType, JsonElement value) {
|
|
return create(property, ImmutableMap.of(), valueType, value);
|
|
}
|
|
|
|
static Vcard create(String property, String valueType, String value) {
|
|
return create(property, valueType, new JsonPrimitive(value));
|
|
}
|
|
|
|
@Override
|
|
public JsonArray toJson() {
|
|
JsonArray jsonArray = new JsonArray();
|
|
jsonArray.add(property());
|
|
jsonArray.add(new Gson().toJsonTree(parameters()));
|
|
jsonArray.add(valueType());
|
|
jsonArray.add(value());
|
|
return jsonArray;
|
|
}
|
|
}
|
|
|
|
@RestrictJsonNames("vcardArray")
|
|
@AutoValue
|
|
abstract static class VcardArray implements Jsonable {
|
|
|
|
private static final String VCARD_VERSION_NUMBER = "4.0";
|
|
private static final Vcard VCARD_ENTRY_VERSION =
|
|
Vcard.create("version", "text", VCARD_VERSION_NUMBER);
|
|
|
|
abstract ImmutableList<Vcard> vcards();
|
|
|
|
@Override
|
|
public JsonArray toJson() {
|
|
JsonArray jsonArray = new JsonArray();
|
|
jsonArray.add("vcard");
|
|
JsonArray jsonVcardsArray = new JsonArray();
|
|
jsonVcardsArray.add(VCARD_ENTRY_VERSION.toJson());
|
|
vcards().forEach(vcard -> jsonVcardsArray.add(vcard.toJson()));
|
|
jsonArray.add(jsonVcardsArray);
|
|
return jsonArray;
|
|
}
|
|
|
|
static Builder builder() {
|
|
return new AutoValue_RdapObjectClasses_VcardArray.Builder();
|
|
}
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder {
|
|
abstract ImmutableList.Builder<Vcard> vcardsBuilder();
|
|
Builder add(Vcard vcard) {
|
|
vcardsBuilder().add(vcard);
|
|
return this;
|
|
}
|
|
|
|
abstract VcardArray build();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Indication of what type of boilerplate notices are required for the RDAP JSON messages. The
|
|
* ICANN RDAP Profile specifies that, for instance, domain name responses should include a remark
|
|
* about domain status codes. So we need to know when to include such boilerplate. On the other
|
|
* hand, remarks are not allowed except in domain, nameserver and entity objects, so we need to
|
|
* suppress them for other types of responses (e.g. help).
|
|
*/
|
|
public enum BoilerplateType {
|
|
DOMAIN,
|
|
NAMESERVER,
|
|
ENTITY,
|
|
OTHER
|
|
}
|
|
|
|
/**
|
|
* An object that can be used to create a TopLevelReply.
|
|
*
|
|
* All Actions need to return an object of this type.
|
|
*/
|
|
@RestrictJsonNames("*")
|
|
abstract static class ReplyPayloadBase extends AbstractJsonableObject {
|
|
final BoilerplateType boilerplateType;
|
|
|
|
ReplyPayloadBase(BoilerplateType boilerplateType) {
|
|
this.boilerplateType = boilerplateType;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The Top Level JSON reply, Adds the required top-level boilerplate to a ReplyPayloadBase.
|
|
*
|
|
* <p>RFC 7483 specifies that the top-level object should include an entry indicating the
|
|
* conformance level. ICANN RDAP spec for 15feb19 mandates several additional entries, in sections
|
|
* 2.6.3, 2.11 of the Response Profile and 3.3.2, 3.5, of the Technical Implementation Guide.
|
|
*/
|
|
@AutoValue
|
|
@RestrictJsonNames({})
|
|
abstract static class TopLevelReplyObject extends AbstractJsonableObject {
|
|
@JsonableElement("rdapConformance")
|
|
static final RdapConformance RDAP_CONFORMANCE = RdapConformance.INSTANCE;
|
|
|
|
@JsonableElement("*") abstract ReplyPayloadBase aAreplyObject();
|
|
@JsonableElement("notices[]") abstract Notice aTosNotice();
|
|
|
|
@JsonableElement("notices") ImmutableList<Notice> boilerplateNotices() {
|
|
switch (aAreplyObject().boilerplateType) {
|
|
case DOMAIN:
|
|
return RdapIcannStandardInformation.domainBoilerplateNotices;
|
|
case NAMESERVER:
|
|
case ENTITY:
|
|
return RdapIcannStandardInformation.nameserverAndEntityBoilerplateNotices;
|
|
default: // things other than domains, nameservers and entities do not yet have boilerplate
|
|
return ImmutableList.of();
|
|
}
|
|
}
|
|
|
|
static TopLevelReplyObject create(ReplyPayloadBase replyObject, Notice tosNotice) {
|
|
return new AutoValue_RdapObjectClasses_TopLevelReplyObject(replyObject, tosNotice);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A base object shared by Entity, Nameserver, and Domain.
|
|
*
|
|
* <p>Not part of the spec, but seems convenient.
|
|
*/
|
|
private abstract static class RdapObjectBase extends ReplyPayloadBase {
|
|
@JsonableElement final ObjectClassName objectClassName;
|
|
|
|
@JsonableElement abstract Optional<String> handle();
|
|
@JsonableElement abstract ImmutableList<PublicId> publicIds();
|
|
@JsonableElement abstract ImmutableList<RdapEntity> entities();
|
|
@JsonableElement abstract ImmutableList<RdapStatus> status();
|
|
@JsonableElement abstract ImmutableList<Remark> remarks();
|
|
@JsonableElement abstract ImmutableList<Link> links();
|
|
@JsonableElement abstract Optional<Port43WhoisServer> port43();
|
|
@JsonableElement abstract ImmutableList<Event> events();
|
|
|
|
RdapObjectBase(BoilerplateType boilerplateType, ObjectClassName objectClassName) {
|
|
super(boilerplateType);
|
|
this.objectClassName = objectClassName;
|
|
}
|
|
|
|
|
|
abstract static class Builder<B extends Builder<?>> {
|
|
abstract B setHandle(String handle);
|
|
abstract ImmutableList.Builder<PublicId> publicIdsBuilder();
|
|
abstract ImmutableList.Builder<RdapEntity> entitiesBuilder();
|
|
abstract ImmutableList.Builder<RdapStatus> statusBuilder();
|
|
abstract ImmutableList.Builder<Remark> remarksBuilder();
|
|
abstract ImmutableList.Builder<Link> linksBuilder();
|
|
abstract B setPort43(Port43WhoisServer port43);
|
|
abstract ImmutableList.Builder<Event> eventsBuilder();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The Entity Object Class defined in 5.1 of RFC7483.
|
|
*
|
|
* <p>We're missing the "autnums" and "networks" fields
|
|
*/
|
|
@RestrictJsonNames({"entities[]", "entitySearchResults[]"})
|
|
@AutoValue
|
|
abstract static class RdapEntity extends RdapObjectBase {
|
|
|
|
/** Role values specified in RFC 7483 § 10.2.4. */
|
|
@RestrictJsonNames("roles[]")
|
|
enum Role implements Jsonable {
|
|
REGISTRANT("registrant"),
|
|
TECH("technical"),
|
|
ADMIN("administrative"),
|
|
ABUSE("abuse"),
|
|
BILLING("billing"),
|
|
REGISTRAR("registrar"),
|
|
RESELLER("reseller"),
|
|
SPONSOR("sponsor"),
|
|
PROXY("proxy"),
|
|
NOTIFICATIONS("notifications"),
|
|
NOC("noc");
|
|
|
|
/** Value as it appears in RDAP messages. */
|
|
final String rfc7483String;
|
|
|
|
Role(String rfc7483String) {
|
|
this.rfc7483String = rfc7483String;
|
|
}
|
|
|
|
@Override
|
|
public JsonPrimitive toJson() {
|
|
return new JsonPrimitive(rfc7483String);
|
|
}
|
|
}
|
|
|
|
RdapEntity() {
|
|
super(BoilerplateType.ENTITY, ObjectClassName.ENTITY);
|
|
}
|
|
|
|
@JsonableElement abstract Optional<VcardArray> vcardArray();
|
|
@JsonableElement abstract ImmutableSet<Role> roles();
|
|
@JsonableElement abstract ImmutableList<EventWithoutActor> asEventActor();
|
|
|
|
static Builder builder() {
|
|
return new AutoValue_RdapObjectClasses_RdapEntity.Builder();
|
|
}
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder extends RdapObjectBase.Builder<Builder> {
|
|
abstract Builder setVcardArray(VcardArray vcardArray);
|
|
abstract ImmutableSet.Builder<Role> rolesBuilder();
|
|
abstract ImmutableList.Builder<EventWithoutActor> asEventActorBuilder();
|
|
|
|
abstract RdapEntity build();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A base object shared by Nameserver, and Domain.
|
|
*
|
|
* <p>Takes care of the name and unicode field.
|
|
*
|
|
* <p>See RDAP Response Profile 15feb19 sections 2.1 and 4.1.
|
|
*
|
|
* <p>Not part of the spec, but seems convenient.
|
|
*/
|
|
private abstract static class RdapNamedObjectBase extends RdapObjectBase {
|
|
|
|
@JsonableElement abstract String ldhName();
|
|
|
|
@JsonableElement final Optional<String> unicodeName() {
|
|
// Only include the unicodeName field if there are unicode characters.
|
|
//
|
|
// TODO(b/127490882) Consider removing the condition (i.e. always having the unicodeName
|
|
// field)
|
|
if (!hasUnicodeComponents(ldhName())) {
|
|
return Optional.empty();
|
|
}
|
|
return Optional.of(Idn.toUnicode(ldhName()));
|
|
}
|
|
|
|
private static boolean hasUnicodeComponents(String fullyQualifiedName) {
|
|
return fullyQualifiedName.startsWith(ACE_PREFIX)
|
|
|| fullyQualifiedName.contains("." + ACE_PREFIX);
|
|
}
|
|
|
|
abstract static class Builder<B extends Builder<?>> extends RdapObjectBase.Builder<B> {
|
|
abstract B setLdhName(String ldhName);
|
|
}
|
|
|
|
RdapNamedObjectBase(BoilerplateType boilerplateType, ObjectClassName objectClassName) {
|
|
super(boilerplateType, objectClassName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The Nameserver Object Class defined in 5.2 of RFC7483.
|
|
*/
|
|
@RestrictJsonNames({"nameservers[]", "nameserverSearchResults[]"})
|
|
@AutoValue
|
|
abstract static class RdapNameserver extends RdapNamedObjectBase {
|
|
|
|
@JsonableElement Optional<IpAddresses> ipAddresses() {
|
|
if (ipv6().isEmpty() && ipv4().isEmpty()) {
|
|
return Optional.empty();
|
|
}
|
|
return Optional.of(new IpAddresses());
|
|
}
|
|
|
|
abstract ImmutableList<String> ipv6();
|
|
abstract ImmutableList<String> ipv4();
|
|
|
|
class IpAddresses extends AbstractJsonableObject {
|
|
@JsonableElement ImmutableList<String> v6() {
|
|
return Ordering.natural().immutableSortedCopy(ipv6());
|
|
}
|
|
|
|
@JsonableElement ImmutableList<String> v4() {
|
|
return Ordering.natural().immutableSortedCopy(ipv4());
|
|
}
|
|
}
|
|
|
|
RdapNameserver() {
|
|
super(BoilerplateType.NAMESERVER, ObjectClassName.NAMESERVER);
|
|
}
|
|
|
|
static Builder builder() {
|
|
return new AutoValue_RdapObjectClasses_RdapNameserver.Builder();
|
|
}
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder extends RdapNamedObjectBase.Builder<Builder> {
|
|
abstract ImmutableList.Builder<String> ipv6Builder();
|
|
abstract ImmutableList.Builder<String> ipv4Builder();
|
|
|
|
abstract RdapNameserver build();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* The Domain Object Class defined in 5.3 of RFC7483.
|
|
*
|
|
* We're missing the "variants", "secureDNS", "network" fields
|
|
*/
|
|
@RestrictJsonNames("domainSearchResults[]")
|
|
@AutoValue
|
|
abstract static class RdapDomain extends RdapNamedObjectBase {
|
|
|
|
@JsonableElement abstract ImmutableList<RdapNameserver> nameservers();
|
|
|
|
RdapDomain() {
|
|
super(BoilerplateType.DOMAIN, ObjectClassName.DOMAIN);
|
|
}
|
|
|
|
static Builder builder() {
|
|
return new AutoValue_RdapObjectClasses_RdapDomain.Builder();
|
|
}
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder extends RdapNamedObjectBase.Builder<Builder> {
|
|
abstract ImmutableList.Builder<RdapNameserver> nameserversBuilder();
|
|
|
|
abstract RdapDomain build();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Error Response Body defined in 6 of RFC7483.
|
|
*/
|
|
@RestrictJsonNames({})
|
|
@AutoValue
|
|
abstract static class ErrorResponse extends ReplyPayloadBase {
|
|
|
|
@JsonableElement final LanguageIdentifier lang = LanguageIdentifier.EN;
|
|
|
|
@JsonableElement abstract int errorCode();
|
|
@JsonableElement abstract String title();
|
|
@JsonableElement abstract ImmutableList<String> description();
|
|
|
|
ErrorResponse() {
|
|
super(BoilerplateType.OTHER);
|
|
}
|
|
|
|
static ErrorResponse create(int status, String title, String description) {
|
|
return new AutoValue_RdapObjectClasses_ErrorResponse(
|
|
status, title, ImmutableList.of(description));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Help Response defined in 7 of RFC7483.
|
|
*
|
|
* <p>The helpNotice field is optional, because if the user requests the TOS - that's already
|
|
* given by the boilerplate of TopLevelReplyObject so we don't want to give it again.
|
|
*/
|
|
@RestrictJsonNames({})
|
|
@AutoValue
|
|
abstract static class HelpResponse extends ReplyPayloadBase {
|
|
@JsonableElement("notices[]") abstract Optional<Notice> helpNotice();
|
|
|
|
HelpResponse() {
|
|
super(BoilerplateType.OTHER);
|
|
}
|
|
|
|
static HelpResponse create(Optional<Notice> helpNotice) {
|
|
return new AutoValue_RdapObjectClasses_HelpResponse(helpNotice);
|
|
}
|
|
}
|
|
|
|
private RdapObjectClasses() {}
|
|
}
|