mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 19:20:44 +02:00
Report BSA block status in DomainCheckFlow (#2288)
- Registered names are not affected. - Reserved names are not affected. - Names that are none of the above and match some BSA labels are reported as blocked.
This commit is contained in:
parent
9273d2bf15
commit
1dcf34ccc2
5 changed files with 98 additions and 6 deletions
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
package google.registry.bsa.persistence;
|
package google.registry.bsa.persistence;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
|
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||||
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
||||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||||
import static google.registry.model.CacheUtils.newCacheBuilder;
|
import static google.registry.model.CacheUtils.newCacheBuilder;
|
||||||
|
@ -22,10 +25,15 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.stream.Streams;
|
||||||
|
|
||||||
/** Helpers for {@link BsaLabel}. */
|
/** Helpers for {@link BsaLabel}. */
|
||||||
public final class BsaLabelUtils {
|
public final class BsaLabelUtils {
|
||||||
|
@ -43,9 +51,11 @@ public final class BsaLabelUtils {
|
||||||
@Override
|
@Override
|
||||||
public Map<VKey<BsaLabel>, Optional<BsaLabel>> loadAll(
|
public Map<VKey<BsaLabel>, Optional<BsaLabel>> loadAll(
|
||||||
Iterable<? extends VKey<BsaLabel>> keys) {
|
Iterable<? extends VKey<BsaLabel>> keys) {
|
||||||
// TODO(b/309173359): need this for DomainCheckFlow
|
ImmutableMap<VKey<? extends BsaLabel>, BsaLabel> existingLabels =
|
||||||
throw new UnsupportedOperationException(
|
replicaTm().reTransact(() -> replicaTm().loadByKeysIfPresent(keys));
|
||||||
"LoadAll not supported by the BsaLabel cache loader.");
|
return Streams.of(keys)
|
||||||
|
.collect(
|
||||||
|
toImmutableMap(key -> key, key -> Optional.ofNullable(existingLabels.get(key))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -84,4 +94,15 @@ public final class BsaLabelUtils {
|
||||||
public static boolean isLabelBlocked(String domainLabel) {
|
public static boolean isLabelBlocked(String domainLabel) {
|
||||||
return cacheBsaLabels.get(BsaLabel.vKey(domainLabel)).isPresent();
|
return cacheBsaLabels.get(BsaLabel.vKey(domainLabel)).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the elements in {@code domainLabels} that are blocked by BSA. */
|
||||||
|
public static ImmutableSet<String> getBlockedLabels(ImmutableCollection<String> domainLabels) {
|
||||||
|
ImmutableList<VKey<BsaLabel>> queriedLabels =
|
||||||
|
domainLabels.stream().map(BsaLabel::vKey).collect(toImmutableList());
|
||||||
|
return cacheBsaLabels.getAll(queriedLabels).values().stream()
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(BsaLabel::getLabel)
|
||||||
|
.collect(toImmutableSet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ public class CheckApiAction implements Runnable {
|
||||||
? "In use"
|
? "In use"
|
||||||
: (isReserved
|
: (isReserved
|
||||||
? reservedError.get()
|
? reservedError.get()
|
||||||
: (isBsaBlocked ? "Blocked by the Brand Safety Alliance" : null));
|
: (isBsaBlocked ? "Blocked by a GlobalBlock service" : null));
|
||||||
|
|
||||||
ImmutableMap.Builder<String, Object> responseBuilder = new ImmutableMap.Builder<>();
|
ImmutableMap.Builder<String, Object> responseBuilder = new ImmutableMap.Builder<>();
|
||||||
metricBuilder.status(SUCCESS).availability(availability);
|
metricBuilder.status(SUCCESS).availability(availability);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import static com.google.common.base.Strings.emptyToNull;
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||||
|
import static google.registry.bsa.persistence.BsaLabelUtils.getBlockedLabels;
|
||||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||||
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
|
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||||
|
@ -34,6 +35,7 @@ import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||||
import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity;
|
import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -83,9 +85,12 @@ import google.registry.model.tld.label.ReservationType;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.pricing.PricingEngineProxy;
|
import google.registry.pricing.PricingEngineProxy;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
@ -177,6 +182,12 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
.build());
|
.build());
|
||||||
ImmutableMap<String, VKey<Domain>> existingDomains =
|
ImmutableMap<String, VKey<Domain>> existingDomains =
|
||||||
ForeignKeyUtils.load(Domain.class, domainNames, now);
|
ForeignKeyUtils.load(Domain.class, domainNames, now);
|
||||||
|
// Check block labels only when there are unregistered domains, since "In use" goes before
|
||||||
|
// "Blocked by BSA".
|
||||||
|
ImmutableSet<InternetDomainName> bsaBlockedDomainNames =
|
||||||
|
existingDomains.size() == parsedDomains.size()
|
||||||
|
? ImmutableSet.of()
|
||||||
|
: getBsaBlockedDomains(parsedDomains.values());
|
||||||
Optional<AllocationTokenExtension> allocationTokenExtension =
|
Optional<AllocationTokenExtension> allocationTokenExtension =
|
||||||
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
eppInput.getSingleExtension(AllocationTokenExtension.class);
|
||||||
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
|
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
|
||||||
|
@ -203,6 +214,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
getMessageForCheck(
|
getMessageForCheck(
|
||||||
parsedDomains.get(domainName),
|
parsedDomains.get(domainName),
|
||||||
existingDomains,
|
existingDomains,
|
||||||
|
bsaBlockedDomainNames,
|
||||||
domainCheckResults,
|
domainCheckResults,
|
||||||
tldStates,
|
tldStates,
|
||||||
allocationToken);
|
allocationToken);
|
||||||
|
@ -234,6 +246,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
private Optional<String> getMessageForCheck(
|
private Optional<String> getMessageForCheck(
|
||||||
InternetDomainName domainName,
|
InternetDomainName domainName,
|
||||||
ImmutableMap<String, VKey<Domain>> existingDomains,
|
ImmutableMap<String, VKey<Domain>> existingDomains,
|
||||||
|
ImmutableSet<InternetDomainName> bsaBlockedDomains,
|
||||||
ImmutableMap<InternetDomainName, String> tokenCheckResults,
|
ImmutableMap<InternetDomainName, String> tokenCheckResults,
|
||||||
ImmutableMap<String, TldState> tldStates,
|
ImmutableMap<String, TldState> tldStates,
|
||||||
Optional<AllocationToken> allocationToken) {
|
Optional<AllocationToken> allocationToken) {
|
||||||
|
@ -251,7 +264,18 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName)));
|
Optional<String> tokenResult =
|
||||||
|
Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName)));
|
||||||
|
if (tokenResult.isPresent()) {
|
||||||
|
return tokenResult;
|
||||||
|
}
|
||||||
|
if (bsaBlockedDomains.contains(domainName)) {
|
||||||
|
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
|
||||||
|
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
|
||||||
|
return Optional.of("Blocked by a GlobalBlock service");
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle the fee check extension. */
|
/** Handle the fee check extension. */
|
||||||
|
@ -415,6 +439,21 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||||
return availabilityCheckDomains;
|
return availabilityCheckDomains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ImmutableSet<InternetDomainName> getBsaBlockedDomains(
|
||||||
|
ImmutableCollection<InternetDomainName> parsedDomains) {
|
||||||
|
Map<String, ImmutableList<InternetDomainName>> labelToDomainNames =
|
||||||
|
parsedDomains.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.groupingBy(
|
||||||
|
parsedDomain -> parsedDomain.parts().get(0), toImmutableList()));
|
||||||
|
ImmutableSet<String> blockedLabels =
|
||||||
|
getBlockedLabels(ImmutableList.copyOf(labelToDomainNames.keySet()));
|
||||||
|
labelToDomainNames.keySet().retainAll(blockedLabels);
|
||||||
|
return labelToDomainNames.values().stream()
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(toImmutableSet());
|
||||||
|
}
|
||||||
|
|
||||||
/** By server policy, fee check names must be listed in the availability check. */
|
/** By server policy, fee check names must be listed in the availability check. */
|
||||||
static class OnlyCheckedNamesCanBeFeeCheckedException extends ParameterValuePolicyErrorException {
|
static class OnlyCheckedNamesCanBeFeeCheckedException extends ParameterValuePolicyErrorException {
|
||||||
OnlyCheckedNamesCanBeFeeCheckedException() {
|
OnlyCheckedNamesCanBeFeeCheckedException() {
|
||||||
|
|
|
@ -296,7 +296,7 @@ class CheckApiActionTest {
|
||||||
"tier", "premium",
|
"tier", "premium",
|
||||||
"status", "success",
|
"status", "success",
|
||||||
"available", false,
|
"available", false,
|
||||||
"reason", "Blocked by the Brand Safety Alliance");
|
"reason", "Blocked by a GlobalBlock service");
|
||||||
|
|
||||||
verifySuccessMetric(PREMIUM, BSA_BLOCKED);
|
verifySuccessMetric(PREMIUM, BSA_BLOCKED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
|
import google.registry.bsa.persistence.BsaTestingUtils;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||||
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
|
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
|
||||||
|
@ -157,6 +158,37 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||||
create(true, "example3.tld", null));
|
create(true, "example3.tld", null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_bsaBlocked_otherwiseAvailable_blocked() throws Exception {
|
||||||
|
BsaTestingUtils.persistBsaLabel("example1", clock.nowUtc());
|
||||||
|
doCheckTest(
|
||||||
|
create(false, "example1.tld", "Blocked by a GlobalBlock service"),
|
||||||
|
create(true, "example2.tld", null),
|
||||||
|
create(true, "example3.tld", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_bsaBlocked_alsoRegistered_registered() throws Exception {
|
||||||
|
BsaTestingUtils.persistBsaLabel("example1", clock.nowUtc());
|
||||||
|
persistActiveDomain("example1.tld");
|
||||||
|
doCheckTest(
|
||||||
|
create(false, "example1.tld", "In use"),
|
||||||
|
create(true, "example2.tld", null),
|
||||||
|
create(true, "example3.tld", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_bsaBlocked_alsoReserved_reserved() throws Exception {
|
||||||
|
BsaTestingUtils.persistBsaLabel("reserved", clock.nowUtc());
|
||||||
|
BsaTestingUtils.persistBsaLabel("allowedinsunrise", clock.nowUtc());
|
||||||
|
setEppInput("domain_check_one_tld_reserved.xml");
|
||||||
|
doCheckTest(
|
||||||
|
create(false, "reserved.tld", "Reserved"),
|
||||||
|
create(false, "allowedinsunrise.tld", "Reserved"),
|
||||||
|
create(true, "example2.tld", null),
|
||||||
|
create(true, "example3.tld", null));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_clTridNotSpecified() throws Exception {
|
void testSuccess_clTridNotSpecified() throws Exception {
|
||||||
setEppInput("domain_check_no_cltrid.xml");
|
setEppInput("domain_check_no_cltrid.xml");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue