1
0
mirror of https://github.com/google/nomulus synced 2026-01-05 04:56:03 +00:00

Add BSA label to rdap-domain 404 responses for BSA domains (#2706)

This commit is contained in:
gbrodman
2025-03-07 08:58:18 -05:00
committed by GitHub
parent d2d43f4115
commit 6b0beeb477
17 changed files with 149 additions and 77 deletions

View File

@@ -199,16 +199,16 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
/** Returns the TLD entities for the given TLD strings, throwing if any don't exist. */ /** Returns the TLD entities for the given TLD strings, throwing if any don't exist. */
public static ImmutableSet<Tld> get(Set<String> tlds) { public static ImmutableSet<Tld> get(Set<String> tlds) {
Map<String, Optional<Tld>> registries = CACHE.getAll(tlds); Map<String, Optional<Tld>> tldObjects = CACHE.getAll(tlds);
ImmutableSet<String> missingRegistries = ImmutableSet<String> missingTlds =
registries.entrySet().stream() tldObjects.entrySet().stream()
.filter(e -> e.getValue().isEmpty()) .filter(e -> e.getValue().isEmpty())
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.collect(toImmutableSet()); .collect(toImmutableSet());
if (missingRegistries.isEmpty()) { if (missingTlds.isEmpty()) {
return registries.values().stream().map(Optional::get).collect(toImmutableSet()); return tldObjects.values().stream().map(Optional::get).collect(toImmutableSet());
} else { } else {
throw new TldNotFoundException(missingRegistries); throw new TldNotFoundException(missingTlds);
} }
} }

View File

@@ -14,10 +14,10 @@
package google.registry.model.tld.label; package google.registry.model.tld.label;
import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.hash.Funnels.stringFunnel; import static com.google.common.hash.Funnels.stringFunnel;
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;

View File

@@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
package google.registry.persistence.converter; package google.registry.persistence.converter;
import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.hash.Funnels.stringFunnel; import static com.google.common.hash.Funnels.stringFunnel;
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.hash.BloomFilter; import com.google.common.hash.BloomFilter;
import jakarta.persistence.AttributeConverter; import jakarta.persistence.AttributeConverter;

View File

@@ -14,14 +14,15 @@
package google.registry.rdap; package google.registry.rdap;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
import static google.registry.request.Actions.getPathForAction; import static google.registry.request.Actions.getPathForAction;
import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static jakarta.servlet.http.HttpServletResponse.SC_OK; import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType; import com.google.common.net.MediaType;
@@ -41,6 +42,7 @@ import google.registry.request.Parameter;
import google.registry.request.RequestMethod; import google.registry.request.RequestMethod;
import google.registry.request.RequestPath; import google.registry.request.RequestPath;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.util.Clock;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Optional; import java.util.Optional;
@@ -60,6 +62,10 @@ public abstract class RdapActionBase implements Runnable {
private static final MediaType RESPONSE_MEDIA_TYPE = private static final MediaType RESPONSE_MEDIA_TYPE =
MediaType.create("application", "rdap+json").withCharset(UTF_8); MediaType.create("application", "rdap+json").withCharset(UTF_8);
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
private static final Gson FORMATTED_OUTPUT_GSON =
new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
/** Whether to include or exclude deleted items from a query. */ /** Whether to include or exclude deleted items from a query. */
protected enum DeletedItemHandling { protected enum DeletedItemHandling {
EXCLUDE, EXCLUDE,
@@ -75,6 +81,7 @@ public abstract class RdapActionBase implements Runnable {
@Inject @Parameter("formatOutput") Optional<Boolean> formatOutputParam; @Inject @Parameter("formatOutput") Optional<Boolean> formatOutputParam;
@Inject @Config("rdapResultSetMaxSize") int rdapResultSetMaxSize; @Inject @Config("rdapResultSetMaxSize") int rdapResultSetMaxSize;
@Inject RdapMetrics rdapMetrics; @Inject RdapMetrics rdapMetrics;
@Inject Clock clock;
/** Builder for metric recording. */ /** Builder for metric recording. */
final RdapMetrics.RdapMetricInformation.Builder metricInformationBuilder = final RdapMetrics.RdapMetricInformation.Builder metricInformationBuilder =
@@ -152,6 +159,10 @@ public abstract class RdapActionBase implements Runnable {
response.setStatus(SC_OK); response.setStatus(SC_OK);
setPayload(replyObject); setPayload(replyObject);
metricInformationBuilder.setStatusCode(SC_OK); metricInformationBuilder.setStatusCode(SC_OK);
} catch (RdapDomainAction.DomainBlockedByBsaException e) {
logger.atInfo().withCause(e).log("Domain blocked by BSA");
setErrorCodes(SC_NOT_FOUND);
setPayload(new RdapObjectClasses.DomainBlockedByBsaErrorResponse(e.getMessage()));
} catch (HttpException e) { } catch (HttpException e) {
logger.atInfo().withCause(e).log("Error in RDAP."); logger.atInfo().withCause(e).log("Error in RDAP.");
setError(e.getResponseCode(), e.getResponseCodeString(), e.getMessage()); setError(e.getResponseCode(), e.getResponseCodeString(), e.getMessage());
@@ -166,8 +177,7 @@ public abstract class RdapActionBase implements Runnable {
} }
void setError(int status, String title, String description) { void setError(int status, String title, String description) {
metricInformationBuilder.setStatusCode(status); setErrorCodes(status);
response.setStatus(status);
try { try {
setPayload(ErrorResponse.create(status, title, description)); setPayload(ErrorResponse.create(status, title, description));
} catch (Exception ex) { } catch (Exception ex) {
@@ -176,21 +186,18 @@ public abstract class RdapActionBase implements Runnable {
} }
} }
void setErrorCodes(int status) {
metricInformationBuilder.setStatusCode(status);
response.setStatus(status);
}
void setPayload(ReplyPayloadBase replyObject) { void setPayload(ReplyPayloadBase replyObject) {
if (requestMethod == Action.Method.HEAD) { if (requestMethod == Action.Method.HEAD) {
return; return;
} }
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.disableHtmlEscaping();
if (formatOutputParam.orElse(false)) {
gsonBuilder.setPrettyPrinting();
}
Gson gson = gsonBuilder.create();
TopLevelReplyObject topLevelObject = TopLevelReplyObject topLevelObject =
TopLevelReplyObject.create(replyObject, rdapJsonFormatter.createTosNotice()); TopLevelReplyObject.create(replyObject, rdapJsonFormatter.createTosNotice());
Gson gson = formatOutputParam.orElse(false) ? FORMATTED_OUTPUT_GSON : GSON;
response.setPayload(gson.toJson(topLevelObject.toJson())); response.setPayload(gson.toJson(topLevelObject.toJson()));
} }

View File

@@ -119,7 +119,7 @@ final class RdapDataStructures {
*/ */
@AutoValue @AutoValue
@RestrictJsonNames("notices[]") @RestrictJsonNames("notices[]")
abstract static class Notice extends NoticeOrRemark { public abstract static class Notice extends NoticeOrRemark {
/** /**
* Notice and Remark Type are defined in 10.2.1 of RFC 9083. * Notice and Remark Type are defined in 10.2.1 of RFC 9083.

View File

@@ -20,8 +20,11 @@ import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD; import static google.registry.request.Action.Method.HEAD;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.tld.Tld;
import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapDomain; import google.registry.rdap.RdapObjectClasses.RdapDomain;
@@ -51,8 +54,9 @@ public class RdapDomainAction extends RdapActionBase {
// RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label // RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label
// (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both. // (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both.
pathSearchString = canonicalizeName(pathSearchString); pathSearchString = canonicalizeName(pathSearchString);
InternetDomainName domainName;
try { try {
validateDomainName(pathSearchString); domainName = validateDomainName(pathSearchString);
} catch (EppException e) { } catch (EppException e) {
throw new BadRequestException( throw new BadRequestException(
String.format( String.format(
@@ -66,6 +70,7 @@ public class RdapDomainAction extends RdapActionBase {
pathSearchString, pathSearchString,
shouldIncludeDeleted() ? START_OF_TIME : rdapJsonFormatter.getRequestTime()); shouldIncludeDeleted() ? START_OF_TIME : rdapJsonFormatter.getRequestTime());
if (domain.isEmpty() || !isAuthorized(domain.get())) { if (domain.isEmpty() || !isAuthorized(domain.get())) {
handlePossibleBsaBlock(domainName);
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the // RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
// query, it MUST reply with 404 response code. // query, it MUST reply with 404 response code.
// //
@@ -75,4 +80,17 @@ public class RdapDomainAction extends RdapActionBase {
} }
return rdapJsonFormatter.createRdapDomain(domain.get(), OutputDataType.FULL); return rdapJsonFormatter.createRdapDomain(domain.get(), OutputDataType.FULL);
} }
private void handlePossibleBsaBlock(InternetDomainName domainName) {
Tld tld = Tld.get(domainName.parent().toString());
if (DomainFlowUtils.isBlockedByBsa(domainName.parts().getFirst(), tld, clock.nowUtc())) {
throw new DomainBlockedByBsaException(domainName + " blocked by BSA");
}
}
static class DomainBlockedByBsaException extends RuntimeException {
DomainBlockedByBsaException(String message) {
super(message);
}
}
} }

View File

@@ -63,8 +63,21 @@ public class RdapIcannStandardInformation {
.build()) .build())
.build(); .build();
/** Not required, but provided when a domain is blocked by BSA. */
private static final Notice DOMAIN_BLOCKED_BY_BSA_NOTICE =
Notice.builder()
.setTitle("Blocked Domain")
.setDescription("This name has been blocked by a GlobalBlock service")
.addLink(
Link.builder()
.setRel("alternate")
.setHref("https://brandsafetyalliance.co")
.setType("text/html")
.build())
.build();
/** Boilerplate notices required by domain responses. */ /** Boilerplate notices required by domain responses. */
static final ImmutableList<Notice> domainBoilerplateNotices = static final ImmutableList<Notice> DOMAIN_BOILERPLATE_NOTICES =
ImmutableList.of( ImmutableList.of(
CONFORMANCE_NOTICE, CONFORMANCE_NOTICE,
// RDAP Response Profile 2.6.3 // RDAP Response Profile 2.6.3
@@ -72,8 +85,12 @@ public class RdapIcannStandardInformation {
// RDAP Response Profile 2.11 // RDAP Response Profile 2.11
INACCURACY_COMPLAINT_FORM_NOTICE); INACCURACY_COMPLAINT_FORM_NOTICE);
/** Boilerplate notice for when a domain is blocked by BSA. */
static final ImmutableList<Notice> DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES =
ImmutableList.of(DOMAIN_BLOCKED_BY_BSA_NOTICE);
/** Boilerplate remarks required by nameserver and entity responses. */ /** Boilerplate remarks required by nameserver and entity responses. */
static final ImmutableList<Notice> nameserverAndEntityBoilerplateNotices = static final ImmutableList<Notice> NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES =
ImmutableList.of(CONFORMANCE_NOTICE); ImmutableList.of(CONFORMANCE_NOTICE);
/** /**

View File

@@ -39,6 +39,7 @@ import google.registry.rdap.RdapDataStructures.RdapConformance;
import google.registry.rdap.RdapDataStructures.RdapStatus; import google.registry.rdap.RdapDataStructures.RdapStatus;
import google.registry.rdap.RdapDataStructures.Remark; import google.registry.rdap.RdapDataStructures.Remark;
import google.registry.util.Idn; import google.registry.util.Idn;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Optional; import java.util.Optional;
/** Object Classes defined in RFC 9083 section 5. */ /** Object Classes defined in RFC 9083 section 5. */
@@ -137,10 +138,22 @@ final class RdapObjectClasses {
* suppress them for other types of responses (e.g. help). * suppress them for other types of responses (e.g. help).
*/ */
public enum BoilerplateType { public enum BoilerplateType {
DOMAIN, DOMAIN(RdapIcannStandardInformation.DOMAIN_BOILERPLATE_NOTICES),
NAMESERVER, DOMAIN_BLOCKED_BY_BSA(RdapIcannStandardInformation.DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES),
ENTITY, NAMESERVER(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
OTHER ENTITY(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
OTHER(ImmutableList.of());
@SuppressWarnings("ImmutableEnumChecker") // immutable lists are, in fact, immutable
private final ImmutableList<Notice> notices;
BoilerplateType(ImmutableList<Notice> notices) {
this.notices = notices;
}
public ImmutableList<Notice> getNotices() {
return notices;
}
} }
/** /**
@@ -173,14 +186,7 @@ final class RdapObjectClasses {
@JsonableElement("notices[]") abstract Notice aTosNotice(); @JsonableElement("notices[]") abstract Notice aTosNotice();
@JsonableElement("notices") ImmutableList<Notice> boilerplateNotices() { @JsonableElement("notices") ImmutableList<Notice> boilerplateNotices() {
return switch (aAreplyObject().boilerplateType) { return aAreplyObject().boilerplateType.getNotices();
case DOMAIN -> RdapIcannStandardInformation.domainBoilerplateNotices;
case NAMESERVER, ENTITY ->
RdapIcannStandardInformation.nameserverAndEntityBoilerplateNotices;
default -> // things other than domains, nameservers and entities do not yet have
// boilerplate
ImmutableList.of();
};
} }
static TopLevelReplyObject create(ReplyPayloadBase replyObject, Notice tosNotice) { static TopLevelReplyObject create(ReplyPayloadBase replyObject, Notice tosNotice) {
@@ -532,6 +538,25 @@ final class RdapObjectClasses {
} }
} }
/** Specialized error response body for when a domain is blocked by BSA. */
@RestrictJsonNames({})
@SuppressWarnings("UnusedVariable")
public static class DomainBlockedByBsaErrorResponse extends ReplyPayloadBase {
@JsonableElement private static final LanguageIdentifier lang = LanguageIdentifier.EN;
@JsonableElement private static final int errorCode = HttpServletResponse.SC_NOT_FOUND;
@JsonableElement private static final String title = "Not Found";
@JsonableElement private final ImmutableList<String> description;
DomainBlockedByBsaErrorResponse(String message) {
super(BoilerplateType.DOMAIN_BLOCKED_BY_BSA);
this.description = ImmutableList.of(message);
}
}
/** Error Response Body defined in 6 of RFC 9083. */ /** Error Response Body defined in 6 of RFC 9083. */
@RestrictJsonNames({}) @RestrictJsonNames({})
@AutoValue @AutoValue

View File

@@ -27,7 +27,7 @@ import java.util.List;
* @param numResourcesRetrieved Number of resources retrieved from the database in the process of * @param numResourcesRetrieved Number of resources retrieved from the database in the process of
* assembling the data set. * assembling the data set.
*/ */
record RdapResultSet<T extends EppResource>( public record RdapResultSet<T extends EppResource>(
ImmutableList<T> resources, ImmutableList<T> resources,
IncompletenessWarningType incompletenessWarningType, IncompletenessWarningType incompletenessWarningType,
int numResourcesRetrieved) { int numResourcesRetrieved) {

View File

@@ -14,9 +14,9 @@
package google.registry.rdap; package google.registry.rdap;
import static com.google.common.base.Charsets.UTF_8;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm; import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
@@ -69,7 +69,7 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
public final BaseSearchResponse getJsonObjectForResource( public final BaseSearchResponse getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest) { String pathSearchString, boolean isHeadRequest) {
// The pathSearchString is not used by search commands. // The pathSearchString is not used by search commands.
if (pathSearchString.length() > 0) { if (!pathSearchString.isEmpty()) {
throw new BadRequestException("Unexpected path"); throw new BadRequestException("Unexpected path");
} }
decodeCursorToken(); decodeCursorToken();
@@ -323,7 +323,8 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
if (partialStringQuery.getHasWildcard()) { if (partialStringQuery.getHasWildcard()) {
builder = builder =
builder.where( builder.where(
filterField, criteriaBuilder::like, filterField,
criteriaBuilder::like,
String.format("%s%%", partialStringQuery.getInitialString())); String.format("%s%%", partialStringQuery.getInitialString()));
} else { } else {
// no wildcard means we use a standard equals query // no wildcard means we use a standard equals query

View File

@@ -40,7 +40,7 @@ import java.util.Optional;
abstract class RdapSearchResults { abstract class RdapSearchResults {
/** Responding To Searches defined in 8 of RFC 9083. */ /** Responding To Searches defined in 8 of RFC 9083. */
abstract static class BaseSearchResponse extends ReplyPayloadBase { public abstract static class BaseSearchResponse extends ReplyPayloadBase {
abstract IncompletenessWarningType incompletenessWarningType(); abstract IncompletenessWarningType incompletenessWarningType();
abstract ImmutableMap<String, URI> navigationLinks(); abstract ImmutableMap<String, URI> navigationLinks();

View File

@@ -20,6 +20,7 @@ import com.google.common.flogger.FluentLogger;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.Nullable;
/** Base for exceptions that cause an HTTP error response. */ /** Base for exceptions that cause an HTTP error response. */
public abstract class HttpException extends RuntimeException { public abstract class HttpException extends RuntimeException {
@@ -33,13 +34,14 @@ public abstract class HttpException extends RuntimeException {
private final int responseCode; private final int responseCode;
protected HttpException(int responseCode, String message, Throwable cause, Level logLevel) { protected HttpException(
int responseCode, String message, @Nullable Throwable cause, Level logLevel) {
super(message, cause); super(message, cause);
this.responseCode = responseCode; this.responseCode = responseCode;
this.logLevel = logLevel; this.logLevel = logLevel;
} }
protected HttpException(int responseCode, String message, Throwable cause) { protected HttpException(int responseCode, String message, @Nullable Throwable cause) {
this(responseCode, message, cause, Level.INFO); this(responseCode, message, cause, Level.INFO);
} }
@@ -117,22 +119,6 @@ public abstract class HttpException extends RuntimeException {
} }
} }
/** Exception that causes a 403 response. */
public static final class ForbiddenException extends HttpException {
public ForbiddenException(String message) {
super(HttpServletResponse.SC_FORBIDDEN, message, null);
}
public ForbiddenException(String message, Exception cause) {
super(HttpServletResponse.SC_FORBIDDEN, message, cause);
}
@Override
public String getResponseCodeString() {
return "Forbidden";
}
}
/** Exception that causes a 404 response. */ /** Exception that causes a 404 response. */
public static final class NotFoundException extends HttpException { public static final class NotFoundException extends HttpException {
public NotFoundException() { public NotFoundException() {
@@ -149,18 +135,6 @@ public abstract class HttpException extends RuntimeException {
} }
} }
/** Exception that causes a 405 response. */
public static final class MethodNotAllowedException extends HttpException {
public MethodNotAllowedException() {
super(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Method not allowed", null);
}
@Override
public String getResponseCodeString() {
return "Method Not Allowed";
}
}
/** Exception that causes a 409 response. */ /** Exception that causes a 409 response. */
public static final class ConflictException extends HttpException { public static final class ConflictException extends HttpException {
public ConflictException(String message) { public ConflictException(String message) {

View File

@@ -13,11 +13,11 @@
// limitations under the License. // limitations under the License.
package google.registry.persistence.converter; package google.registry.persistence.converter;
import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.hash.Funnels.stringFunnel; import static com.google.common.hash.Funnels.stringFunnel;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.insertInDb; import static google.registry.testing.DatabaseHelper.insertInDb;
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.hash.BloomFilter; import com.google.common.hash.BloomFilter;

View File

@@ -21,7 +21,6 @@ import static google.registry.testing.DatabaseHelper.insertSimpleResources;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testcontainers.containers.PostgreSQLContainer.POSTGRESQL_PORT; import static org.testcontainers.containers.PostgreSQLContainer.POSTGRESQL_PORT;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -305,7 +304,7 @@ public abstract class JpaTransactionManagerExtension
private static String readSqlInClassPath(String sqlScriptPath) { private static String readSqlInClassPath(String sqlScriptPath) {
try { try {
return Resources.toString(Resources.getResource(sqlScriptPath), Charsets.UTF_8); return Resources.toString(Resources.getResource(sqlScriptPath), UTF_8);
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }

View File

@@ -85,6 +85,7 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(clock); action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(clock);
action.rdapMetrics = rdapMetrics; action.rdapMetrics = rdapMetrics;
action.requestMethod = GET; action.requestMethod = GET;
action.clock = new FakeClock(DateTime.parse("2025-01-01T00:00:00.000Z"));
logout(); logout();
} }

View File

@@ -15,6 +15,7 @@
package google.registry.rdap; package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain; import static google.registry.testing.DatabaseHelper.persistActiveDomain;
@@ -27,8 +28,11 @@ import static google.registry.testing.FullFieldsTestEntityHelper.makeHistoryEntr
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar; import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar;
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarPocs; import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarPocs;
import static google.registry.testing.GsonSubject.assertAboutJson; import static google.registry.testing.GsonSubject.assertAboutJson;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
@@ -608,6 +612,34 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.build()); .build());
} }
@Test
void testBlockedByBsa() {
persistResource(
Tld.get("lol").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistBsaLabel("example");
ImmutableMap<?, ?> expectedBsaNotice =
ImmutableMap.of(
"description",
ImmutableList.of("This name has been blocked by a GlobalBlock service"),
"title",
"Blocked Domain",
"links",
ImmutableList.of(
ImmutableMap.of(
"href",
"https://brandsafetyalliance.co",
"rel",
"alternate",
"type",
"text/html")));
JsonObject expectedErrorResponse = generateExpectedJsonError("example.lol blocked by BSA", 404);
expectedErrorResponse
.getAsJsonArray("notices")
.add(RdapTestHelper.GSON.toJsonTree(expectedBsaNotice));
assertAboutJson().that(generateActualJson("example.lol")).isEqualTo(expectedErrorResponse);
assertThat(response.getStatus()).isEqualTo(404);
}
private Domain persistActiveDomainWithHost( private Domain persistActiveDomainWithHost(
String label, String tld, DateTime creationTime, DateTime expirationTime) { String label, String tld, DateTime creationTime, DateTime expirationTime) {
return persistResource( return persistResource(

View File

@@ -33,8 +33,7 @@ import java.util.Map;
/** Test helper methods for RDAP tests. */ /** Test helper methods for RDAP tests. */
class RdapTestHelper { class RdapTestHelper {
private static final Gson GSON = static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
static JsonElement createJson(String... lines) { static JsonElement createJson(String... lines) {
return GSON.fromJson(Joiner.on("\n").join(lines), JsonElement.class); return GSON.fromJson(Joiner.on("\n").join(lines), JsonElement.class);
@@ -240,5 +239,4 @@ class RdapTestHelper {
obj.remove("rdapConformance"); obj.remove("rdapConformance");
return reply; return reply;
} }
} }