1
0
mirror of https://github.com/google/nomulus synced 2026-07-20 23:12:40 +00:00

Compare commits

..

20 Commits

Author SHA1 Message Date
Lai Jiang 6e201450f0 Remove ofy support from PollMessage (#1732)
Also deletes the autorenew poll message history revision id field in
Domain, which is only needed to recreate the ofy key for the poll
message. The column already contains null values in it, making it
impossible to depend on it. The column itself will be deleted from the
schema after this PR is deployed.

The logic to update autorenew recurrence end time is changed
accordingly: When a poll message already exists, we simply update the
endtime, but when it no longer exists, i. e. when it's deleted
speculatively after a transfer request, we recreate one using the
history entry id that resulted in its creation (e. g. cancelled or rejected
transfer).

This should fix b/240984498. Though the exact reason for that bug is
still unclear to me. Namely, it throws an NPE at this line during an
explicit domain transfer approval:

https://cs.opensource.google/nomulus/nomulus/+/master:core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java;l=603;bpv=1;bpt=0;drc=ede919d7dcdb7f209b074563b3d449ebee19118a

The domain in question has a null autorenewPollMessageHistoryId, but
that in itself should not have caused an NPE because we are not
operating on the null pointer. On that line the only possible way to
throw an NPE is for the domain itself to be null, but if that were the
case, the NPE would have been thrown at line 599 where we called a
method on the domain object.

Regardless of the cause, with this PR we are using an explicitly
provided history id and checking for its nullness before using it. If a
similar issue arises again, we should have a better idea why.

Lastly, the way poll message id is constructed is largely simplified in
PollMessageExternalKeyConverter as a result of the removal ofy parent
keys in PollMessage. This does present a possibility of failure when
immediately before deployment, a registrar requests a poll message and
received the old id, but by the time the registrar acks the id, the new
version is deployed and therefore does not recognize the old key. The
likelihood of this happening should be slim, and we could have prevented
it by letting the converter recognize both the old and the new key.
However, we would like to eventually phase out the old key, and in
theory a registrar could ack a poll message at any time after it was
requested. So, there is not a safe time by which all the old ids are
acked, lest we develop some elaborate scheme to keep track of which
messages were sent with an old id when requested and which of these old
ids are acked. Only then can we be truly safe to phase out the old id.
The benefit does not seem to warrant the effort. If a registrar does
encounter a situation like this, they could open a support bug to have
us manually ack the poll message for them.
2022-08-19 14:24:03 -04:00
sarahcaseybot dda9a3ef7e Flyway files for PackagePromotion table (#1746)
* Include missing file

* Fix merge conflicts

* make package price non null
2022-08-19 12:53:58 -04:00
gbrodman 8c1fb6bf00 Add another TestCacheExtension usage (#1750) 2022-08-19 10:17:15 -04:00
gbrodman 4e21152f04 Add TestCacheExtension in ResourceFlowTC to fix flakes (#1749)
Basically, what's happening here is that some flow tests are adding
things to the claims list cache which is stored statically, meaning that
some other tests can pick those up when they shouldn't. By adding the
extension in RFTC, it'll clear out the caches after each test.
2022-08-18 15:04:29 -04:00
gbrodman 22193474d5 Add Flyway and golden files for console User object (#1747) 2022-08-17 16:48:14 -04:00
Pavlo Tkach efd5244ebd Add email notification when DNS update fails (#1734) 2022-08-16 12:59:08 -04:00
gbrodman 87e5d19fe5 Allow anchor tenant creation via allocation token behavior (#1735)
* Allow anchor tenant creation via allocation token behavior

This also enforces that non-superusers cannot create registrations on
trademarked names prior to the sunrise period, even if they have an
allocation token with ANCHOR_TENANT behavior.
2022-08-15 12:42:16 -04:00
Lai Jiang bbb6174c9f Remove InjectExtension (#1739)
It is only used to set the clock used by Ofy, and it admits itself being
an ugly hack...

Also applied IntelliJ suggestions on touched files.
2022-08-12 14:56:53 -04:00
gbrodman 2b826651e6 Create a registry lock permission and corresponding account manager role (#1740)
* Create a registry lock permission and corresponding account manager role

This allows us to distinguish between standard account managers and
users that might have the registry lock permission. This will make the
registry lock password-setting flow easier (user can reset their
password iff they have the REGISTRY_LOCK permission, instead of having a
separate boolean) and allows us to easily determine whether or not a
user should have access to registry lock views in the UI.
2022-08-12 12:18:09 -04:00
Lai Jiang e4132db8ed Delete SetClockExtension (#1738)
We no longer write to commit logs, and the ReplayExtension that this
extension is supposed to be used with is already deleted.
2022-08-11 13:16:30 -04:00
Pavlo Tkach 45d90e7c68 Extend IP validation test with message verification (#1736) 2022-08-10 13:27:55 -04:00
sarahcaseybot 028005906a Add allocation token to transfer command (#1737) 2022-08-09 15:18:23 -04:00
sarahcaseybot 78d78e21cb Add CurrentPackageToken to Domain table (#1720)
* Add allocation token to Domain table

* Add tests

* Change column name

* change test names
2022-08-09 11:23:21 -04:00
Lai Jiang 2f3ac2e43b Remove unused columns in BillingCancellation (#1721)
We stopped using these columns after BillingVKey is removed in
https://github.com/google/nomulus/pull/1710.
2022-08-08 10:28:30 -04:00
gbrodman 632e3831e5 Add caches to ClaimsListDao and ClaimsList (#1731)
We cache the ClaimsList Java object for six hours (we don't expect it to
change frequently, and the cron job to update it only runs every twelve
hours). Subsequent calls to ClaimsListDao::get will return the cached
value.

Within the ClaimsList Java object itself, we cache any labels that we
have retrieved. While we already have a form of a cache here in the
"labelsToKeys" map, that only handles situations where we've loaded the
entire map from the database. We want to have a non-guaranteed cache in
order to make repeated calls to getClaimKey fast.
2022-08-05 17:29:59 -04:00
Ben McIlwain 9ff25f9a67 Make domain transfers use (and retain) the renewal price/behavior (#1701)
* Use the new renewal price logic in transfer flow

* Fix build

* Add renewal handling on all transfer flows

* Merge branch 'master' into transfer-retain-renewal-price

* Merge branch 'master' into transfer-retain-renewal-price

* Add more tests
2022-08-05 15:53:27 -04:00
gbrodman eb1a314666 Add base object classes for new user/role permissioning model (#1707)
* Add base object classes for new user/role permissioning model

- Adds the permissions themselves
- Adds the six roles that a user may have -- three global, three
  per-registrar
- Adds the mapping from role -> set of permissions
- Adds a UserRoles object to encapsulate the answer to the question of
  "does this user have this permission?"
- Adds a User class as a base to show how we will use the new UserRoles
  object
2022-08-05 14:18:16 -04:00
Ben McIlwain 0e182546f9 Rename HostResource -> Host (#1733)
* Rename HostResource -> Host
2022-08-05 10:28:45 -04:00
Pavlo Tkach ad06ba2e1e Extend registrar allowed IPs auth exception text with IP address (#1726) 2022-08-03 15:24:00 -04:00
Lai Jiang c903ed4c13 For some reason after the upgrade to Gradle, the core.jar file is no (#1730)
longer included in the generated WAR, even though the deploy_jar
configuration is specified as a dependency.

I could not figure out a way to tweak the configuration dependency to
have core.jar pulled into the .war, so I decided to just explicitly pick
it from its known location.

TESTED=deployed to alpha and verified that the instances can start.
2022-08-03 10:26:44 -04:00
233 changed files with 7261 additions and 5303 deletions
+6
View File
@@ -26,6 +26,7 @@ project.convention.plugins['war'].webAppDirName =
apply plugin: 'com.google.cloud.tools.appengine'
def coreResourcesDir = "${rootDir}/core/build/resources/main"
def coreLibsDir = "${rootDir}/core/build/libs"
// Get the web.xml file for the service.
war {
@@ -38,6 +39,10 @@ war {
from("${coreResourcesDir}/google/registry/ui/html") {
include "*.html"
}
from("${coreLibsDir}") {
include "core.jar"
into("WEB-INF/lib")
}
}
if (project.path == ":services:default") {
@@ -98,6 +103,7 @@ rootProject.deploy.dependsOn appengineDeployAll
rootProject.stage.dependsOn appengineStage
tasks['war'].dependsOn ':core:compileProdJS'
tasks['war'].dependsOn ':core:processResources'
tasks['war'].dependsOn ':core:jar'
// Impose verification for all of the deployment tasks. We haven't found a
// better way to do this other than to apply to each of them independently.
@@ -29,7 +29,7 @@ import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.EppResource;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.request.Action.Service;
import google.registry.util.CloudTasksUtils;
@@ -140,8 +140,8 @@ public final class AsyncTaskEnqueuer {
}
/** Enqueues a task to asynchronously refresh DNS for a renamed host. */
public void enqueueAsyncDnsRefresh(HostResource host, DateTime now) {
VKey<HostResource> hostKey = host.createVKey();
public void enqueueAsyncDnsRefresh(Host host, DateTime now) {
VKey<Host> hostKey = host.createVKey();
logger.atInfo().log("Enqueuing async DNS refresh for renamed host %s.", hostKey);
addTaskToQueueWithRetry(
asyncDnsRefreshPullQueue,
@@ -31,7 +31,7 @@ import google.registry.model.EppResource;
import google.registry.model.EppResourceUtils;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao;
@@ -43,8 +43,8 @@ import google.registry.util.Clock;
import javax.inject.Inject;
/**
* Hard deletes load-test ContactResources, HostResources, their subordinate history entries, and
* the associated ForeignKey and EppResourceIndex entities.
* Hard deletes load-test ContactResources, Hosts, their subordinate history entries, and the
* associated ForeignKey and EppResourceIndex entities.
*
* <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link
* DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note
@@ -93,7 +93,7 @@ public class DeleteLoadTestDataAction implements Runnable {
() -> {
LOAD_TEST_REGISTRARS.forEach(this::deletePollMessages);
tm().loadAllOfStream(ContactResource.class).forEach(this::deleteContact);
tm().loadAllOfStream(HostResource.class).forEach(this::deleteHost);
tm().loadAllOfStream(Host.class).forEach(this::deleteHost);
});
}
@@ -123,11 +123,11 @@ public class DeleteLoadTestDataAction implements Runnable {
deleteResource(contact);
}
private void deleteHost(HostResource host) {
private void deleteHost(Host host) {
if (!LOAD_TEST_REGISTRARS.contains(host.getPersistedCurrentSponsorRegistrarId())) {
return;
}
VKey<HostResource> hostVKey = host.createVKey();
VKey<Host> hostVKey = host.createVKey();
// We can remove hosts from linked domains, so we should do so then delete the hosts
ImmutableSet<VKey<Domain>> linkedDomains =
EppResourceUtils.getLinkedDomainKeys(hostVKey, clock.nowUtc(), null);
@@ -135,7 +135,7 @@ public class DeleteLoadTestDataAction implements Runnable {
.values()
.forEach(
domain -> {
ImmutableSet<VKey<HostResource>> remainingHosts =
ImmutableSet<VKey<Host>> remainingHosts =
domain.getNsHosts().stream()
.filter(vkey -> !vkey.equals(hostVKey))
.collect(toImmutableSet());
@@ -49,8 +49,8 @@ import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.Type;
@@ -148,7 +148,7 @@ import org.joda.time.DateTime;
* then loaded from the remaining referenced contact histories, and marshalled into (pending
* deposit: deposit fragment) pairs.
*
* <h3>{@link HostResource}</h3>
* <h3>{@link Host}</h3>
*
* Similar to {@link ContactResource}, we join the most recent host history with referenced hosts to
* find most recent referenced hosts. For external hosts we do the same treatment as we did on
@@ -467,10 +467,9 @@ public class RdePipeline implements Serializable {
Counter activeDomainCounter = Metrics.counter("RDE", "ActiveDomainBase");
Counter domainFragmentCounter = Metrics.counter("RDE", "DomainFragment");
Counter referencedContactCounter = Metrics.counter("RDE", "ReferencedContactResource");
Counter referencedHostCounter = Metrics.counter("RDE", "ReferencedHostResource");
Counter referencedHostCounter = Metrics.counter("RDE", "ReferencedHost");
return domainHistories.apply(
"Map DomainHistory to DepositFragment "
+ "and emit referenced ContactResource and HostResource",
"Map DomainHistory to DepositFragment " + "and emit referenced ContactResource and Host",
ParDo.of(
new DoFn<KV<String, Long>, KV<PendingDeposit, DepositFragment>>() {
@ProcessElement
@@ -565,10 +564,10 @@ public class RdePipeline implements Serializable {
private PCollectionTuple processHostHistories(
PCollection<KV<String, PendingDeposit>> referencedHosts,
PCollection<KV<String, Long>> hostHistories) {
Counter subordinateHostCounter = Metrics.counter("RDE", "SubordinateHostResource");
Counter externalHostCounter = Metrics.counter("RDE", "ExternalHostResource");
Counter subordinateHostCounter = Metrics.counter("RDE", "SubordinateHost");
Counter externalHostCounter = Metrics.counter("RDE", "ExternalHost");
Counter externalHostFragmentCounter = Metrics.counter("RDE", "ExternalHostFragment");
return removeUnreferencedResource(referencedHosts, hostHistories, HostResource.class)
return removeUnreferencedResource(referencedHosts, hostHistories, Host.class)
.apply(
"Map external DomainResource to DepositFragment and process subordinate domains",
ParDo.of(
@@ -576,8 +575,8 @@ public class RdePipeline implements Serializable {
@ProcessElement
public void processElement(
@Element KV<String, CoGbkResult> kv, MultiOutputReceiver receiver) {
HostResource host =
(HostResource)
Host host =
(Host)
loadResourceByHistoryEntryId(
HostHistory.class,
kv.getKey(),
@@ -631,11 +630,9 @@ public class RdePipeline implements Serializable {
Counter referencedSubordinateHostCounter = Metrics.counter("RDE", "ReferencedSubordinateHost");
return KeyedPCollectionTuple.of(HOST_TO_PENDING_DEPOSIT, superordinateDomains)
.and(REVISION_ID, domainHistories)
.apply("Join Host:PendingDeposits with DomainHistory on Domain", CoGroupByKey.create())
.apply(
"Join HostResource:PendingDeposits with DomainHistory on DomainResource",
CoGroupByKey.create())
.apply(
" Remove unreferenced DomainResource",
" Remove unreferenced Domain",
Filter.by(
kv -> {
boolean toInclude =
@@ -647,7 +644,7 @@ public class RdePipeline implements Serializable {
return toInclude;
}))
.apply(
"Map subordinate HostResource to DepositFragment",
"Map subordinate Host to DepositFragment",
FlatMapElements.into(
kvs(
TypeDescriptor.of(PendingDeposit.class),
@@ -664,8 +661,8 @@ public class RdePipeline implements Serializable {
new ImmutableSet.Builder<>();
for (KV<String, CoGbkResult> hostToPendingDeposits :
kv.getValue().getAll(HOST_TO_PENDING_DEPOSIT)) {
HostResource host =
(HostResource)
Host host =
(Host)
loadResourceByHistoryEntryId(
HostHistory.class,
hostToPendingDeposits.getKey(),
@@ -25,7 +25,7 @@ import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.util.DateTimeUtils;
@@ -52,7 +52,7 @@ import org.joda.time.DateTime;
public class ResaveAllEppResourcesPipeline implements Serializable {
private static final ImmutableSet<Class<? extends EppResource>> EPP_RESOURCE_CLASSES =
ImmutableSet.of(ContactResource.class, Domain.class, HostResource.class);
ImmutableSet.of(ContactResource.class, Domain.class, Host.class);
/**
* There exist three possible situations where we know we'll want to project domains to the
@@ -1300,6 +1300,30 @@ public final class RegistryConfig {
return config.sslCertificateValidation.expirationWarningEmailSubjectText;
}
@Provides
@Config("dnsUpdateFailEmailSubjectText")
public static String provideDnsUpdateFailEmailSubjectText(RegistryConfigSettings config) {
return config.dnsUpdate.dnsUpdateFailEmailSubjectText;
}
@Provides
@Config("dnsUpdateFailEmailBodyText")
public static String provideDnsUpdateFailEmailBodyText(RegistryConfigSettings config) {
return config.dnsUpdate.dnsUpdateFailEmailBodyText;
}
@Provides
@Config("dnsUpdateFailRegistryName")
public static String provideDnsUpdateFailRegistryName(RegistryConfigSettings config) {
return config.dnsUpdate.dnsUpdateFailRegistryName;
}
@Provides
@Config("registrySupportEmail")
public static String provideRegistrySupportEmail(RegistryConfigSettings config) {
return config.dnsUpdate.registrySupportEmail;
}
@Provides
@Config("allowedEcdsaCurves")
public static ImmutableSet<String> provideAllowedEcdsaCurves(RegistryConfigSettings config) {
@@ -1427,6 +1451,11 @@ public final class RegistryConfig {
return CONFIG_SETTINGS.get().caching.eppResourceMaxCachedEntries;
}
/** Returns the amount of time that a particular claims list should be cached. */
public static java.time.Duration getClaimsListCacheDuration() {
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.claimsListCachingSeconds);
}
/** Returns the email address that outgoing emails from the app are sent from. */
public static InternetAddress getGSuiteOutgoingEmailAddress() {
return parseEmailAddress(CONFIG_SETTINGS.get().gSuite.outgoingEmailAddress);
@@ -42,6 +42,7 @@ public class RegistryConfigSettings {
public RegistryTool registryTool;
public SslCertificateValidation sslCertificateValidation;
public ContactHistory contactHistory;
public DnsUpdate dnsUpdate;
/** Configuration options that apply to the entire App Engine project. */
public static class AppEngine {
@@ -155,6 +156,7 @@ public class RegistryConfigSettings {
public boolean eppResourceCachingEnabled;
public int eppResourceCachingSeconds;
public int eppResourceMaxCachedEntries;
public int claimsListCachingSeconds;
}
/** Configuration for ICANN monthly reporting. */
@@ -245,4 +247,12 @@ public class RegistryConfigSettings {
public int minMonthsBeforeWipeOut;
public int wipeOutQueryBatchSize;
}
/** Configuration for dns update. */
public static class DnsUpdate {
public String dnsUpdateFailEmailSubjectText;
public String dnsUpdateFailEmailBodyText;
public String dnsUpdateFailRegistryName;
public String registrySupportEmail;
}
}
@@ -294,6 +294,10 @@ caching:
# have to be very large to achieve the vast majority of possible gains.
eppResourceMaxCachedEntries: 500
# Length of time that a claims list will be cached after retrieval. A fairly
# long duration is acceptable because claims lists don't change frequently.
claimsListCachingSeconds: 21600 # six hours
oAuth:
# OAuth scopes to detect on access tokens. Superset of requiredOauthScopes.
availableOauthScopes:
@@ -473,6 +477,28 @@ contactHistory:
# The batch size for querying ContactHistory table in the database.
wipeOutQueryBatchSize: 500
# Configuration options relevant to the DNS update functionality.
dnsUpdate:
dnsUpdateFailRegistryName: Example name
registrySupportEmail: email@example.com
# Email subject text template to notify partners after repeatedly failing DNS update
dnsUpdateFailEmailSubjectText: "[ACTION REQUIRED]: Incomplete DNS Update"
# Email body text template for failing DNS update that accepts 5 parameters:
# registrar name, domain or host address, 'domain' or 'host' as a string that failed,
# registry support email (see dnsUpdateFailRegistrySupportEmail) and registry display name
dnsUpdateFailEmailBodyText: >
Dear %1$s,
We are contacting you regarding the changes you recently made to one of your %2$ss.
The DNS update for the %3$s %2$s failed to process. Please review your %2$s's DNS records
and ensure that it is valid before trying another update.
If you have any questions or require additional support, please contact us
at %4$s.
Regards,
%5$s
# Configuration options for checking SSL certificates.
sslCertificateValidation:
# A map specifying the maximum amount of days the certificate can be valid.
@@ -14,6 +14,7 @@
package google.registry.dns;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
@@ -22,6 +23,7 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_CREATED;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.request.Action.Method.POST;
import static google.registry.request.RequestParameters.PARAM_TLD;
import static google.registry.util.CollectionUtils.nullToEmpty;
@@ -37,6 +39,10 @@ import google.registry.dns.DnsMetrics.ActionStatus;
import google.registry.dns.DnsMetrics.CommitStatus;
import google.registry.dns.DnsMetrics.PublishStatus;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.tld.Registry;
import google.registry.request.Action;
import google.registry.request.Action.Service;
@@ -46,6 +52,7 @@ import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
import google.registry.ui.server.SendEmailUtils;
import google.registry.util.Clock;
import google.registry.util.CloudTasksUtils;
import google.registry.util.DomainNameUtils;
@@ -102,6 +109,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
private final Clock clock;
private final CloudTasksUtils cloudTasksUtils;
private final Response response;
private final SendEmailUtils sendEmailUtils;
private final String dnsUpdateFailEmailSubjectText;
private final String dnsUpdateFailEmailBodyText;
private final String dnsUpdateFailRegistryName;
private final String registrySupportEmail;
@Inject
public PublishDnsUpdatesAction(
@@ -114,6 +126,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
@Parameter(PARAM_HOSTS) Set<String> hosts,
@Parameter(PARAM_TLD) String tld,
@Config("publishDnsUpdatesLockDuration") Duration timeout,
@Config("dnsUpdateFailEmailSubjectText") String dnsUpdateFailEmailSubjectText,
@Config("dnsUpdateFailEmailBodyText") String dnsUpdateFailEmailBodyText,
@Config("dnsUpdateFailRegistryName") String dnsUpdateFailRegistryName,
@Config("registrySupportEmail") String registrySupportEmail,
@Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount,
@Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount,
DnsQueue dnsQueue,
@@ -122,11 +138,13 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
LockHandler lockHandler,
Clock clock,
CloudTasksUtils cloudTasksUtils,
SendEmailUtils sendEmailUtils,
Response response) {
this.dnsQueue = dnsQueue;
this.dnsWriterProxy = dnsWriterProxy;
this.dnsMetrics = dnsMetrics;
this.timeout = timeout;
this.sendEmailUtils = sendEmailUtils;
this.retryCount =
cloudTasksRetryCount.orElse(
appEngineRetryCount.orElseThrow(
@@ -143,6 +161,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
this.clock = clock;
this.cloudTasksUtils = cloudTasksUtils;
this.response = response;
this.dnsUpdateFailEmailBodyText = dnsUpdateFailEmailBodyText;
this.dnsUpdateFailEmailSubjectText = dnsUpdateFailEmailSubjectText;
this.dnsUpdateFailRegistryName = dnsUpdateFailRegistryName;
this.registrySupportEmail = registrySupportEmail;
}
private void recordActionResult(ActionStatus status) {
@@ -209,9 +231,35 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
} else if (retryCount < RETRIES_BEFORE_PERMANENT_FAILURE) {
// If the batch only contains 1 name, allow it more retries
throw e;
} else {
// By the time we get here there's either single domain or a single host
domains.stream()
.findFirst()
.ifPresent(
dn -> {
Optional<Domain> domain = loadByForeignKey(Domain.class, dn, clock.nowUtc());
if (domain.isPresent()) {
notifyWithEmailAboutDnsUpdateFailure(
domain.get().getCurrentSponsorRegistrarId(), dn, false);
} else {
logger.atSevere().log(String.format("Domain entity for %s not found", dn));
}
});
hosts.stream()
.findFirst()
.ifPresent(
hn -> {
Optional<Host> host = loadByForeignKey(Host.class, hn, clock.nowUtc());
if (host.isPresent()) {
notifyWithEmailAboutDnsUpdateFailure(
host.get().getPersistedCurrentSponsorRegistrarId(), hn, true);
} else {
logger.atSevere().log(String.format("Host entity for %s not found", hn));
}
});
}
// If we get here, we should terminate this task as it is likely a perpetually failing task.
// TODO(b/237302821): Send an email notifying partner the dns update failed
recordActionResult(ActionStatus.MAX_RETRIES_EXCEEDED);
response.setStatus(SC_ACCEPTED);
logger.atSevere().withCause(e).log("Terminated task after too many retries");
@@ -219,6 +267,30 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
return null;
}
/** Sends an email to partners regarding a failure during DNS update */
private void notifyWithEmailAboutDnsUpdateFailure(
String registrarId, String hostOrDomainName, Boolean isHost) {
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(registrarId);
if (registrar.isPresent()) {
sendEmailUtils.sendEmail(
dnsUpdateFailEmailSubjectText,
String.format(
dnsUpdateFailEmailBodyText,
registrar.get().getRegistrarName(),
hostOrDomainName,
isHost ? "host" : "domain",
registrySupportEmail,
dnsUpdateFailRegistryName),
registrar.get().getContacts().stream()
.filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN))
.map(RegistrarPoc::getEmailAddress)
.collect(toImmutableList()));
} else {
logger.atSevere().log(String.format("Could not find registrar %s", registrarId));
}
}
/** Splits the domains and hosts in a batch into smaller batches and adds them to the queue. */
private void splitBatch() {
ImmutableList<String> domainList = ImmutableList.copyOf(domains);
@@ -294,8 +366,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
int domainsPublished = 0;
int domainsRejected = 0;
for (String domain : nullToEmpty(domains)) {
if (!DomainNameUtils.isUnder(
InternetDomainName.from(domain), InternetDomainName.from(tld))) {
if (!DomainNameUtils.isUnder(InternetDomainName.from(domain), InternetDomainName.from(tld))) {
logger.atSevere().log("%s: skipping domain %s not under TLD.", tld, domain);
domainsRejected += 1;
} else {
@@ -310,8 +381,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
int hostsPublished = 0;
int hostsRejected = 0;
for (String host : nullToEmpty(hosts)) {
if (!DomainNameUtils.isUnder(
InternetDomainName.from(host), InternetDomainName.from(tld))) {
if (!DomainNameUtils.isUnder(InternetDomainName.from(host), InternetDomainName.from(tld))) {
logger.atSevere().log("%s: skipping host %s not under TLD.", tld, host);
hostsRejected += 1;
} else {
@@ -21,7 +21,7 @@ import google.registry.model.EppResource;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
@@ -66,7 +66,7 @@ public final class RefreshDnsAction implements Runnable {
dnsQueue.addDomainRefreshTask(domainOrHostName);
break;
case HOST:
verifyHostIsSubordinate(loadAndVerifyExistence(HostResource.class, domainOrHostName));
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
dnsQueue.addHostRefreshTask(domainOrHostName);
break;
default:
@@ -86,7 +86,7 @@ public final class RefreshDnsAction implements Runnable {
domainOrHostName)));
}
private static void verifyHostIsSubordinate(HostResource host) {
private static void verifyHostIsSubordinate(Host host) {
if (!host.isSubordinate()) {
throw new BadRequestException(
String.format("%s isn't a subordinate hostname", host.getHostName()));
@@ -38,7 +38,7 @@ import google.registry.dns.writer.DnsWriter;
import google.registry.dns.writer.DnsWriterZone;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.tld.Registries;
import google.registry.util.Clock;
import google.registry.util.Concurrent;
@@ -190,7 +190,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
// Load the target host. Note that it can be absent if this host was just deleted.
// desiredRecords is populated with an empty set to indicate that all existing records
// should be deleted.
Optional<HostResource> host = loadByForeignKey(HostResource.class, hostName, clock.nowUtc());
Optional<Host> host = loadByForeignKey(Host.class, hostName, clock.nowUtc());
// Return early if the host is deleted.
if (!host.isPresent()) {
@@ -29,7 +29,7 @@ import google.registry.dns.writer.BaseDnsWriter;
import google.registry.dns.writer.DnsWriterZone;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.tld.Registries;
import google.registry.util.Clock;
import java.io.IOException;
@@ -215,7 +215,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
private void addInBailiwickNameServerSet(Domain domain, Update update) {
for (String hostName :
intersection(domain.loadNameserverHostNames(), domain.getSubordinateHosts())) {
Optional<HostResource> host = loadByForeignKey(HostResource.class, hostName, clock.nowUtc());
Optional<Host> host = loadByForeignKey(Host.class, hostName, clock.nowUtc());
checkState(host.isPresent(), "Host %s cannot be loaded", hostName);
update.add(makeAddressSet(host.get()));
update.add(makeV6AddressSet(host.get()));
@@ -236,7 +236,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
return nameServerSet;
}
private RRset makeAddressSet(HostResource host) {
private RRset makeAddressSet(Host host) {
RRset addressSet = new RRset();
for (InetAddress address : host.getInetAddresses()) {
if (address instanceof Inet4Address) {
@@ -252,7 +252,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
return addressSet;
}
private RRset makeV6AddressSet(HostResource host) {
private RRset makeV6AddressSet(Host host) {
RRset addressSet = new RRset();
for (InetAddress address : host.getInetAddresses()) {
if (address instanceof Inet6Address) {
@@ -26,7 +26,7 @@
<property name="creationTime" direction="desc"/>
</datastore-index>
<!-- For finding host resources by registrar. -->
<datastore-index kind="HostResource" ancestor="false" source="manual">
<datastore-index kind="Host" ancestor="false" source="manual">
<property name="currentSponsorClientId" direction="asc"/>
<property name="deletionTime" direction="asc"/>
<property name="fullyQualifiedHostName" direction="asc"/>
@@ -52,7 +52,7 @@
<property name="deletionTime" direction="asc"/>
</datastore-index>
<!-- For WHOIS IP address lookup -->
<datastore-index kind="HostResource" ancestor="false" source="manual">
<datastore-index kind="Host" ancestor="false" source="manual">
<property name="inetAddresses" direction="asc"/>
<property name="deletionTime" direction="asc"/>
</datastore-index>
@@ -87,7 +87,7 @@
<property name="tld" direction="asc"/>
<property name="fullyQualifiedDomainName" direction="asc"/>
</datastore-index>
<datastore-index kind="HostResource" ancestor="false" source="manual">
<datastore-index kind="Host" ancestor="false" source="manual">
<property name="deletionTime" direction="asc"/>
<property name="fullyQualifiedHostName" direction="asc"/>
</datastore-index>
@@ -114,7 +114,7 @@ public class TlsCredentials implements TransportCredentials {
"Authentication error: IP address %s is not allow-listed for registrar %s; allow list is:"
+ " %s",
clientInetAddr, registrar.getRegistrarId(), ipAddressAllowList);
throw new BadRegistrarIpAddressException();
throw new BadRegistrarIpAddressException(clientInetAddr);
}
@VisibleForTesting
@@ -216,8 +216,13 @@ public class TlsCredentials implements TransportCredentials {
/** Registrar IP address is not in stored allow list. */
public static class BadRegistrarIpAddressException extends AuthenticationErrorException {
BadRegistrarIpAddressException() {
super("Registrar IP address is not in stored allow list");
BadRegistrarIpAddressException(Optional<InetAddress> clientInetAddr) {
super(
clientInetAddr.isPresent()
? String.format(
"Registrar IP address %s is not in stored allow list",
clientInetAddr.get().getHostAddress())
: "Registrar IP address is not in stored allow list");
}
}
@@ -153,6 +153,7 @@ import org.joda.time.Duration;
* @error {@link DomainCreateFlow.AnchorTenantCreatePeriodException}
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
@@ -376,8 +377,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
.setIdnTableName(validateDomainNameWithIdnTables(domainName))
.setRegistrationExpirationTime(registrationExpirationTime)
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
.setAutorenewPollMessage(
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
.setSmdId(signedMarkId)
.setDsData(secDnsCreate.map(SecDnsCreateExtension::getDsData).orElse(null))
@@ -481,7 +481,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
boolean isValidReservedCreate,
boolean hasSignedMarks)
throws NoGeneralRegistrationsInCurrentPhaseException,
MustHaveSignedMarksInCurrentPhaseException {
MustHaveSignedMarksInCurrentPhaseException,
NoTrademarkedRegistrationsBeforeSunriseException {
// We allow general registration during GA.
TldState currentState = registry.getTldState(now);
if (currentState.equals(GENERAL_AVAILABILITY)) {
@@ -496,16 +497,23 @@ public final class DomainCreateFlow implements TransactionalFlow {
// Bypass most TLD state checks if that behavior is specified by the token
if (behavior.equals(RegistrationBehavior.BYPASS_TLD_STATE)
|| behavior.equals(RegistrationBehavior.ANCHOR_TENANT)) {
// If bypassing TLD state checks, a post-sunrise state is always fine
if (!currentState.equals(START_DATE_SUNRISE)
&& registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
return;
}
// Non-trademarked names with the state check bypassed are always available
if (!claimsList.getClaimKey(domainLabel).isPresent()) {
return;
}
if (!currentState.equals(START_DATE_SUNRISE)) {
// Trademarked domains cannot be registered until after the sunrise period has ended, unless
// a valid signed mark is provided. Signed marks can only be provided during sunrise.
// Thus, when bypassing TLD state checks, a post-sunrise state is always fine.
if (registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
return;
} else {
// If sunrise hasn't happened yet, trademarked domains are unavailable
throw new NoTrademarkedRegistrationsBeforeSunriseException(domainLabel);
}
}
}
// Otherwise, signed marks are necessary and sufficient in the sunrise period
if (currentState.equals(START_DATE_SUNRISE)) {
if (!hasSignedMarks) {
@@ -724,6 +732,17 @@ public final class DomainCreateFlow implements TransactionalFlow {
}
}
/** Trademarked domains cannot be registered before the sunrise period. */
static class NoTrademarkedRegistrationsBeforeSunriseException
extends ParameterValuePolicyErrorException {
public NoTrademarkedRegistrationsBeforeSunriseException(String domainLabel) {
super(
String.format(
"The trademarked label %s cannot be registered before the sunrise period.",
domainLabel));
}
}
/** Anchor tenant domain create is for the wrong number of years. */
static class AnchorTenantCreatePeriodException extends ParameterValuePolicyErrorException {
public AnchorTenantCreatePeriodException(int invalidYears) {
@@ -64,6 +64,7 @@ import google.registry.flows.custom.DomainDeleteFlowCustomLogic.BeforeSaveParame
import google.registry.flows.custom.EntityChanges;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
@@ -260,8 +261,10 @@ public final class DomainDeleteFlow implements TransactionalFlow {
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
// Close the autorenew billing event and poll message. This may delete the poll message. Store
// the updated recurring billing event, we'll need it later and can't reload it.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
BillingEvent.Recurring recurringBillingEvent =
updateAutorenewRecurrenceEndTime(existingDomain, now);
updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, now, domainHistory.getDomainHistoryId());
// If there's a pending transfer, the gaining client's autorenew billing
// event and poll message will already have been deleted in
// ResourceDeleteFlow since it's listed in serverApproveEntities.
@@ -112,9 +112,10 @@ import google.registry.model.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
@@ -270,7 +271,13 @@ public class DomainFlowUtils {
&& token.get().getDomainName().get().equals(domainName.toString())) {
return true;
}
// Otherwise check whether the metadata extension is being used by a superuser to specify that
// Otherwise, check to see if we're using the specialized anchor tenant registration behavior on
// the allocation token
if (token.isPresent()
&& token.get().getRegistrationBehavior().equals(RegistrationBehavior.ANCHOR_TENANT)) {
return true;
}
// Otherwise, check whether the metadata extension is being used by a superuser to specify that
// it's an anchor tenant creation.
return metadataExtension.isPresent() && metadataExtension.get().getIsAnchorTenant();
}
@@ -375,7 +382,7 @@ public class DomainFlowUtils {
static void verifyNotInPendingDelete(
Set<DesignatedContact> contacts,
VKey<ContactResource> registrant,
Set<VKey<HostResource>> nameservers)
Set<VKey<Host>> nameservers)
throws EppException {
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
@@ -583,7 +590,11 @@ public class DomainFlowUtils {
*
* <p>Returns the new autorenew recurring billing event.
*/
public static Recurring updateAutorenewRecurrenceEndTime(Domain domain, DateTime newEndTime) {
public static Recurring updateAutorenewRecurrenceEndTime(
Domain domain,
Recurring existingRecurring,
DateTime newEndTime,
@Nullable DomainHistoryId historyId) {
Optional<PollMessage.Autorenew> autorenewPollMessage =
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
@@ -591,33 +602,34 @@ public class DomainFlowUtils {
// create a new one at the same id. This can happen if a transfer was requested on a domain
// where all autorenew poll messages had already been delivered (this would cause the poll
// message to be deleted), and then subsequently the transfer was canceled, rejected, or deleted
// (which would cause the poll message to be recreated here).
PollMessage.Autorenew updatedAutorenewPollMessage =
autorenewPollMessage.isPresent()
? autorenewPollMessage.get().asBuilder().setAutorenewEndTime(newEndTime).build()
: newAutorenewPollMessage(domain)
.setId((Long) domain.getAutorenewPollMessage().getSqlKey())
.setAutorenewEndTime(newEndTime)
.setDomainHistoryId(
new DomainHistoryId(
domain.getRepoId(), domain.getAutorenewPollMessageHistoryId()))
.build();
// (which would cause the poll message to be recreated here). In the latter case, the history id
// of the event that created the new poll message will also be used.
PollMessage.Autorenew updatedAutorenewPollMessage;
if (autorenewPollMessage.isPresent()) {
updatedAutorenewPollMessage =
autorenewPollMessage.get().asBuilder().setAutorenewEndTime(newEndTime).build();
} else {
checkNotNull(
historyId, "Cannot create a new autorenew poll message without a domain history id");
updatedAutorenewPollMessage =
newAutorenewPollMessage(domain)
.setId((Long) domain.getAutorenewPollMessage().getSqlKey())
.setAutorenewEndTime(newEndTime)
.setDomainHistoryId(historyId)
.build();
}
// If the resultant autorenew poll message would have no poll messages to deliver, then just
// delete it. Otherwise save it with the new end time.
// delete it. Otherwise, save it with the new end time.
if (isAtOrAfter(updatedAutorenewPollMessage.getEventTime(), newEndTime)) {
autorenewPollMessage.ifPresent(autorenew -> tm().delete(autorenew));
} else {
tm().put(updatedAutorenewPollMessage);
}
Recurring recurring =
tm().loadByKey(domain.getAutorenewBillingEvent())
.asBuilder()
.setRecurrenceEndTime(newEndTime)
.build();
tm().put(recurring);
return recurring;
Recurring newRecurring = existingRecurring.asBuilder().setRecurrenceEndTime(newEndTime).build();
tm().put(newRecurring);
return newRecurring;
}
/**
@@ -708,7 +720,10 @@ public class DomainFlowUtils {
throw new TransfersAreAlwaysForOneYearException();
}
builder.setAvailIfSupported(true);
fees = pricingLogic.getTransferPrice(registry, domainNameString, now).getFees();
fees =
pricingLogic
.getTransferPrice(registry, domainNameString, now, recurringBillingEvent)
.getFees();
break;
case UPDATE:
builder.setAvailIfSupported(true);
@@ -767,7 +782,7 @@ public class DomainFlowUtils {
final Optional<? extends FeeTransformCommandExtension> feeCommand,
FeesAndCredits feesAndCredits)
throws EppException {
if (isDomainPremium(domainName, priceTime) && !feeCommand.isPresent()) {
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
throw new FeesRequiredForPremiumNameException();
}
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
@@ -195,9 +195,14 @@ public final class DomainPricingLogic {
}
/** Returns a new transfer price for the pricer. */
FeesAndCredits getTransferPrice(Registry registry, String domainName, DateTime dateTime)
FeesAndCredits getTransferPrice(
Registry registry,
String domainName,
DateTime dateTime,
@Nullable Recurring recurringBillingEvent)
throws EppException {
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
FeesAndCredits renewPrice =
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
return customLogic.customizeTransferPrice(
TransferPriceParameters.newBuilder()
.setFeesAndCredits(
@@ -205,9 +210,9 @@ public final class DomainPricingLogic {
.setCurrency(registry.getCurrency())
.addFeeOrCredit(
Fee.create(
domainPrices.getRenewCost().getAmount(),
renewPrice.getRenewCost().getAmount(),
FeeType.RENEW,
domainPrices.isPremium()))
renewPrice.hasAnyPremiumFees()))
.build())
.setRegistry(registry)
.setDomainName(InternetDomainName.from(domainName))
@@ -222,7 +222,12 @@ public final class DomainRenewFlow implements TransactionalFlow {
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build();
// End the old autorenew billing event and poll message now. This may delete the poll message.
updateAutorenewRecurrenceEndTime(existingDomain, now);
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime(
existingDomain,
existingRecurring,
now,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
Domain newDomain =
existingDomain
.asBuilder()
@@ -230,9 +235,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
.setLastEppUpdateRegistrarId(registrarId)
.setRegistrationExpirationTime(newExpirationTime)
.setAutorenewBillingEvent(newAutorenewEvent.createVKey())
.setAutorenewPollMessage(
newAutorenewPollMessage.createVKey(),
newAutorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(newAutorenewPollMessage.createVKey())
.addGracePeriod(
GracePeriod.forBillingEvent(
GracePeriodStatus.RENEW, existingDomain.getRepoId(), explicitRenewEvent))
@@ -251,8 +251,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
.setGracePeriods(null)
.setDeletePollMessage(null)
.setAutorenewBillingEvent(autorenewEvent.createVKey())
.setAutorenewPollMessage(
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
// Clear the autorenew end time so if it had expired but is now explicitly being restored,
// it won't immediately be deleted again.
.setAutorenewEndTime(Optional.empty())
@@ -31,7 +31,6 @@ import static google.registry.model.ResourceTransferUtils.approvePendingTransfer
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
@@ -45,17 +44,21 @@ import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationTokenExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord;
@@ -85,6 +88,18 @@ import org.joda.time.DateTime;
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
* @error {@link google.registry.flows.exceptions.NotPendingTransferException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
*/
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_APPROVE)
public final class DomainTransferApproveFlow implements TransactionalFlow {
@@ -96,6 +111,10 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
@Inject @Superuser boolean isSuperuser;
@Inject DomainHistory.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainPricingLogic pricingLogic;
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject EppInput eppInput;
@Inject DomainTransferApproveFlow() {}
/**
@@ -104,11 +123,17 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
*/
@Override
public EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class);
extensionManager.register(MetadataExtension.class, AllocationTokenExtension.class);
validateRegistrarIsLoggedIn(registrarId);
extensionManager.validate();
DateTime now = tm().getTransactionTime();
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
existingDomain,
Registry.get(existingDomain.getTld()),
registrarId,
now,
eppInput.getSingleExtension(AllocationTokenExtension.class));
verifyOptionalAuthInfo(authInfo, existingDomain);
verifyHasPendingTransfer(existingDomain);
verifyResourceOwnership(registrarId, existingDomain);
@@ -120,6 +145,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
String gainingRegistrarId = transferData.getGainingRegistrarId();
// Create a transfer billing event for 1 year, unless the superuser extension was used to set
// the transfer period to zero. There is not a transfer cost if the transfer period is zero.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class);
historyBuilder.setId(domainHistoryKey.getId());
Optional<BillingEvent.OneTime> billingEvent =
@@ -131,7 +157,14 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.setTargetId(targetId)
.setRegistrarId(gainingRegistrarId)
.setPeriodYears(1)
.setCost(getDomainRenewCost(targetId, transferData.getTransferRequestTime(), 1))
.setCost(
pricingLogic
.getTransferPrice(
Registry.get(tld),
targetId,
transferData.getTransferRequestTime(),
existingRecurring)
.getRenewCost())
.setEventTime(now)
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
.setDomainHistoryId(
@@ -145,9 +178,9 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
getOnlyElement(existingDomain.getGracePeriodsOfType(GracePeriodStatus.AUTO_RENEW), null);
if (autorenewGrace != null) {
// During a normal transfer, if the domain is in the auto-renew grace period, the auto-renew
// billing event is cancelled and the gaining registrar is charged for the one year renewal.
// billing event is cancelled and the gaining registrar is charged for the one-year renewal.
// But, if the superuser extension is used to request a transfer without an additional year
// then the gaining registrar is not charged for the one year renewal and the losing registrar
// then the gaining registrar is not charged for the one-year renewal and the losing registrar
// still needs to be charged for the auto-renew.
if (billingEvent.isPresent()) {
entitiesToSave.add(
@@ -161,7 +194,11 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
}
// Close the old autorenew event and poll message at the transfer time (aka now). This may end
// up deleting the poll message.
updateAutorenewRecurrenceEndTime(existingDomain, now);
updateAutorenewRecurrenceEndTime(
existingDomain,
existingRecurring,
now,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
DateTime newExpirationTime =
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
// Create a new autorenew event starting at the expiration time.
@@ -172,6 +209,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.setTargetId(targetId)
.setRegistrarId(gainingRegistrarId)
.setEventTime(newExpirationTime)
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistoryId(
new DomainHistoryId(
@@ -205,9 +244,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.build())
.setRegistrationExpirationTime(newExpirationTime)
.setAutorenewBillingEvent(autorenewEvent.createVKey())
.setAutorenewPollMessage(
gainingClientAutorenewPollMessage.createVKey(),
gainingClientAutorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(gainingClientAutorenewPollMessage.createVKey())
// Remove all the old grace periods and add a new one for the transfer.
.setGracePeriods(
billingEvent
@@ -40,6 +40,7 @@ import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.metadata.MetadataExtension;
@@ -114,7 +115,9 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
targetId, newDomain.getTransferData(), null, domainHistoryKey));
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
// may recreate the autorenew poll message if it was deleted when the transfer request was made.
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId());
// Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved.
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
@@ -42,6 +42,7 @@ import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.metadata.MetadataExtension;
@@ -115,7 +116,9 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
targetId, newDomain.getTransferData(), null, now, domainHistoryKey));
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
// may end up recreating the poll message if it was deleted upon the transfer request.
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId());
// Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved.
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
@@ -47,19 +47,23 @@ import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.flows.exceptions.AlreadyPendingTransferException;
import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
import google.registry.flows.exceptions.ObjectAlreadySponsoredException;
import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Transfer;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.Period;
import google.registry.model.domain.fee.FeeTransferCommandExtension;
import google.registry.model.domain.fee.FeeTransformResponseExtension;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.superuser.DomainTransferRequestSuperuserExtension;
import google.registry.model.domain.token.AllocationTokenExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
@@ -116,6 +120,18 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException}
*/
@ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST)
public final class DomainTransferRequestFlow implements TransactionalFlow {
@@ -137,6 +153,8 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainPricingLogic pricingLogic;
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject DomainTransferRequestFlow() {}
@Override
@@ -144,12 +162,19 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
extensionManager.register(
DomainTransferRequestSuperuserExtension.class,
FeeTransferCommandExtension.class,
MetadataExtension.class);
MetadataExtension.class,
AllocationTokenExtension.class);
validateRegistrarIsLoggedIn(gainingClientId);
verifyRegistrarIsActive(gainingClientId);
extensionManager.validate();
DateTime now = tm().getTransactionTime();
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
existingDomain,
Registry.get(existingDomain.getTld()),
gainingClientId,
now,
eppInput.getSingleExtension(AllocationTokenExtension.class));
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
Period period =
@@ -168,10 +193,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
throw new TransferPeriodZeroAndFeeTransferExtensionException();
}
// If the period is zero, then there is no fee for the transfer.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
Optional<FeesAndCredits> feesAndCredits =
(period.getValue() == 0)
? Optional.empty()
: Optional.of(pricingLogic.getTransferPrice(registry, targetId, now));
: Optional.of(
pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
if (feesAndCredits.isPresent()) {
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get());
}
@@ -180,9 +207,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
.setId(domainHistoryKey.getId())
.setOtherRegistrarId(existingDomain.getCurrentSponsorRegistrarId());
DateTime automaticTransferTime =
superuserExtension.isPresent()
? now.plusDays(superuserExtension.get().getAutomaticTransferLength())
: now.plus(registry.getAutomaticTransferLength());
superuserExtension
.map(
domainTransferRequestSuperuserExtension ->
now.plusDays(
domainTransferRequestSuperuserExtension.getAutomaticTransferLength()))
.orElseGet(() -> now.plus(registry.getAutomaticTransferLength()));
// If the domain will be in the auto-renew grace period at the moment of transfer, the transfer
// will subsume the autorenew, so we don't add the normal extra year from the transfer.
// The gaining registrar is still billed for the extra year; the losing registrar will get a
@@ -201,6 +231,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
serverApproveNewExpirationTime,
domainHistoryKey,
existingDomain,
existingRecurring,
trid,
gainingClientId,
feesAndCredits.map(FeesAndCredits::getTotalCost),
@@ -230,7 +261,11 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
// the poll message if it has no events left. Note that if the automatic transfer succeeds, then
// cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
// that we've created in this flow and stored in pendingTransferData.
updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime);
updateAutorenewRecurrenceEndTime(
existingDomain,
existingRecurring,
automaticTransferTime,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
Domain newDomain =
existingDomain
.asBuilder()
@@ -294,7 +329,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
*
* <p>Even if not required, this policy is desirable because it dramatically simplifies the logic
* in transfer flows. Registrars appear to never request 2+ year transfers in practice, and they
* can always decompose an multi-year transfer into a 1-year transfer followed by a manual renewal
* can always decompose a multi-year transfer into a 1-year transfer followed by a manual renewal
* afterwards. The <a href="https://tools.ietf.org/html/rfc5731#section-3.2.4">EPP Domain RFC,
* section 3.2.4</a> says about EPP transfer periods that "the number of units available MAY be
* subject to limits imposed by the server" so we're just limiting the units to one.
@@ -339,7 +374,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
private DomainTransferResponse createResponse(
Period period, Domain existingDomain, Domain newDomain, DateTime now) {
// If the registration were approved this instant, this is what the new expiration would be,
// because we cap at 10 years from the moment of approval. This is different than the server
// because we cap at 10 years from the moment of approval. This is different from the server
// approval new expiration time, which is capped at 10 years from the server approve time.
DateTime approveNowExtendedRegistrationTime =
computeExDateForApprovalTime(existingDomain, now, period);
@@ -24,6 +24,7 @@ import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
@@ -110,6 +111,7 @@ public final class DomainTransferUtils {
DateTime serverApproveNewExpirationTime,
Key<DomainHistory> domainHistoryKey,
Domain existingDomain,
Recurring existingRecurring,
Trid trid,
String gainingRegistrarId,
Optional<Money> transferCost,
@@ -144,7 +146,11 @@ public final class DomainTransferUtils {
return builder
.add(
createGainingClientAutorenewEvent(
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
existingRecurring,
serverApproveNewExpirationTime,
domainHistoryKey,
targetId,
gainingRegistrarId))
.add(
createGainingClientAutorenewPollMessage(
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
@@ -239,6 +245,7 @@ public final class DomainTransferUtils {
}
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
Recurring existingRecurring,
DateTime serverApproveNewExpirationTime,
Key<DomainHistory> domainHistoryKey,
String targetId,
@@ -250,6 +257,8 @@ public final class DomainTransferUtils {
.setRegistrarId(gainingRegistrarId)
.setEventTime(serverApproveNewExpirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
.setDomainHistoryId(
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build();
@@ -30,8 +30,8 @@ import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CheckData.HostCheck;
import google.registry.model.eppoutput.CheckData.HostCheckData;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostCommand.Check;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.util.Clock;
import javax.inject.Inject;
@@ -61,8 +61,7 @@ public final class HostCheckFlow implements Flow {
extensionManager.validate(); // There are no legal extensions for this flow.
ImmutableList<String> hostnames = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(hostnames, maxChecks);
ImmutableSet<String> existingIds =
checkResourcesExist(HostResource.class, hostnames, clock.nowUtc());
ImmutableSet<String> existingIds = checkResourcesExist(Host.class, hostnames, clock.nowUtc());
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
for (String hostname : hostnames) {
boolean unused = !existingIds.contains(hostname);
@@ -46,9 +46,9 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.HostCreateData;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostCommand.Create;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
@@ -105,7 +105,7 @@ public final class HostCreateFlow implements TransactionalFlow {
extensionManager.validate();
Create command = (Create) resourceCommand;
DateTime now = tm().getTransactionTime();
verifyResourceDoesNotExist(HostResource.class, targetId, now, registrarId);
verifyResourceDoesNotExist(Host.class, targetId, now, registrarId);
// The superordinate domain of the host object if creating an in-bailiwick host, or null if
// creating an external host. This is looked up before we actually create the Host object so
// we can detect error conditions earlier.
@@ -121,8 +121,8 @@ public final class HostCreateFlow implements TransactionalFlow {
? new SubordinateHostMustHaveIpException()
: new UnexpectedExternalHostIpException();
}
HostResource newHost =
new HostResource.Builder()
Host newHost =
new Host.Builder()
.setCreationRegistrarId(registrarId)
.setPersistedCurrentSponsorRegistrarId(registrarId)
.setHostName(targetId)
@@ -38,8 +38,8 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import javax.inject.Inject;
@@ -92,8 +92,8 @@ public final class HostDeleteFlow implements TransactionalFlow {
extensionManager.validate();
DateTime now = tm().getTransactionTime();
validateHostName(targetId);
checkLinkedDomains(targetId, now, HostResource.class);
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
checkLinkedDomains(targetId, now, Host.class);
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
if (!isSuperuser) {
// Hosts transfer with their superordinate domains, so for hosts with a superordinate domain,
@@ -104,8 +104,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
: existingHost;
verifyResourceOwnership(registrarId, owningResource);
}
HostResource newHost =
existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
Host newHost = existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
if (existingHost.isSubordinate()) {
dnsQueue.addHostRefreshTask(existingHost.getHostName());
tm().update(
@@ -30,8 +30,8 @@ import google.registry.flows.annotations.ReportingSpec;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostInfoData;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.util.Clock;
import javax.inject.Inject;
@@ -65,7 +65,7 @@ public final class HostInfoFlow implements Flow {
extensionManager.validate(); // There are no legal extensions for this flow.
validateHostName(targetId);
DateTime now = clock.nowUtc();
HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now);
Host host = loadAndVerifyExistence(Host.class, targetId, now);
ImmutableSet.Builder<StatusValue> statusValues = new ImmutableSet.Builder<>();
statusValues.addAll(host.getStatusValues());
if (isLinked(host.createVKey(), now)) {
@@ -52,11 +52,11 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostCommand.Update;
import google.registry.model.host.HostCommand.Update.AddRemove;
import google.registry.model.host.HostCommand.Update.Change;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.persistence.VKey;
@@ -133,7 +133,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
String suppliedNewHostName = change.getFullyQualifiedHostName();
DateTime now = tm().getTransactionTime();
validateHostName(targetId);
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
boolean isHostRename = suppliedNewHostName != null;
String oldHostName = targetId;
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
@@ -148,7 +148,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
EppResource owningResource = firstNonNull(oldSuperordinateDomain, existingHost);
verifyUpdateAllowed(
command, existingHost, newSuperordinateDomain.orElse(null), owningResource, isHostRename);
if (isHostRename && loadAndGetKey(HostResource.class, newHostName, now) != null) {
if (isHostRename && loadAndGetKey(Host.class, newHostName, now) != null) {
throw new HostAlreadyExistsException(newHostName);
}
AddRemove add = command.getInnerAdd();
@@ -175,7 +175,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
newSuperordinateDomain.isPresent()
? newSuperordinateDomain.get().getCurrentSponsorRegistrarId()
: owningResource.getPersistedCurrentSponsorRegistrarId();
HostResource newHost =
Host newHost =
existingHost
.asBuilder()
.setHostName(newHostName)
@@ -210,7 +210,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
private void verifyUpdateAllowed(
Update command,
HostResource existingHost,
Host existingHost,
Domain newSuperordinateDomain,
EppResource owningResource,
boolean isHostRename)
@@ -237,8 +237,8 @@ public final class HostUpdateFlow implements TransactionalFlow {
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
}
private void verifyHasIpsIffIsExternal(
Update command, HostResource existingHost, HostResource newHost) throws EppException {
private void verifyHasIpsIffIsExternal(Update command, Host existingHost, Host newHost)
throws EppException {
boolean wasSubordinate = existingHost.isSubordinate();
boolean willBeSubordinate = newHost.isSubordinate();
boolean willBeExternal = !willBeSubordinate;
@@ -258,7 +258,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
}
}
private void enqueueTasks(HostResource existingHost, HostResource newHost) {
private void enqueueTasks(Host existingHost, Host newHost) {
// Only update DNS for subordinate hosts. External hosts have no glue to write, so they
// are only written as NS records from the referencing domain.
if (existingHost.isSubordinate()) {
@@ -277,7 +277,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
}
}
private static void updateSuperordinateDomains(HostResource existingHost, HostResource newHost) {
private static void updateSuperordinateDomains(Host existingHost, Host newHost) {
if (existingHost.isSubordinate()
&& newHost.isSubordinate()
&& Objects.equals(
@@ -41,7 +41,7 @@ public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeS
* that this is updated on every save, rather than only in response to an {@code <update>} command
*/
@XmlTransient
// Prevents subclasses from unexpectedly accessing as property (e.g., HostResource), which would
// Prevents subclasses from unexpectedly accessing as property (e.g., Host), which would
// require an unnecessary non-private setter method.
@Access(AccessType.FIELD)
@AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp"))
@@ -23,12 +23,11 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PollMessage;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
@@ -55,12 +54,9 @@ public final class EntityClasses {
ForeignKeyIndex.ForeignKeyHostIndex.class,
GaeUserIdConverter.class,
HistoryEntry.class,
Host.class,
HostHistory.class,
HostResource.class,
Lock.class,
PollMessage.class,
PollMessage.Autorenew.class,
PollMessage.OneTime.class,
RdeRevision.class,
Registrar.class,
ServerSecret.class);
@@ -35,7 +35,7 @@ import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao;
@@ -345,8 +345,8 @@ public final class EppResourceUtils {
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
VKey<? extends EppResource> key, DateTime now, @Nullable Integer limit) {
checkArgument(
key.getKind().equals(ContactResource.class) || key.getKind().equals(HostResource.class),
"key must be either VKey<ContactResource> or VKey<HostResource>, but it is %s",
key.getKind().equals(ContactResource.class) || key.getKind().equals(Host.class),
"key must be either VKey<ContactResource> or VKey<Host>, but it is %s",
key);
boolean isContactKey = key.getKind().equals(ContactResource.class);
if (tm().isOfy()) {
@@ -26,7 +26,7 @@ import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.secdns.DomainDsDataHistory;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTransactionManager;
@@ -68,7 +68,7 @@ public class BulkQueryEntities {
DomainLite domainLite,
ImmutableSet<GracePeriod> gracePeriods,
ImmutableSet<DelegationSignerData> delegationSignerData,
ImmutableSet<VKey<HostResource>> nsHosts) {
ImmutableSet<VKey<Host>> nsHosts) {
Domain.Builder builder = new Domain.Builder();
builder.copyFrom(domainLite);
builder.setGracePeriods(gracePeriods);
@@ -82,7 +82,7 @@ public class BulkQueryEntities {
public static DomainHistory assembleDomainHistory(
DomainHistoryLite domainHistoryLite,
ImmutableSet<DomainDsDataHistory> dsDataHistories,
ImmutableSet<VKey<HostResource>> domainHistoryHosts,
ImmutableSet<VKey<Host>> domainHistoryHosts,
ImmutableSet<GracePeriodHistory> gracePeriodHistories,
ImmutableSet<DomainTransactionRecord> transactionRecords) {
DomainHistory.Builder builder = new DomainHistory.Builder();
@@ -16,7 +16,7 @@ package google.registry.model.bulkquery;
import com.google.common.base.Objects;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import java.io.Serializable;
import javax.persistence.Access;
@@ -44,8 +44,8 @@ public class DomainHistoryHost implements Serializable {
return new DomainHistoryId(domainHistoryDomainRepoId, domainHistoryHistoryRevisionId);
}
public VKey<HostResource> getHostVKey() {
return VKey.create(HostResource.class, hostRepoId);
public VKey<Host> getHostVKey() {
return VKey.create(Host.class, hostRepoId);
}
@Override
@@ -15,7 +15,7 @@
package google.registry.model.bulkquery;
import com.google.common.base.Objects;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import java.io.Serializable;
import javax.persistence.Access;
@@ -40,8 +40,8 @@ public class DomainHost implements Serializable {
return domainRepoId;
}
public VKey<HostResource> getHostVKey() {
return VKey.create(HostResource.class, hostRepoId);
public VKey<Host> getHostVKey() {
return VKey.create(Host.class, hostRepoId);
}
@Override
@@ -0,0 +1,69 @@
// Copyright 2022 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.model.console;
/** Permissions that users may have in the UI, either per-registrar or globally. */
public enum ConsolePermission {
/** Add, update, or remove other console users. */
MANAGE_USERS,
/** Add, update, or remove registrars. */
MANAGE_REGISTRARS,
/** Manage related registrars, e.g. when one registrar owns another. */
MANAGE_ACCREDITATION,
/** Set up the EPP connection (e.g. certs). */
CONFIGURE_EPP_CONNECTION,
/** Retrieve the unredacted registrant email from a domain. */
GET_REGISTRANT_EMAIL,
/** Suspend a domain for compliance (non-URS) reasons. */
SUSPEND_DOMAIN,
/** Suspend a domain for the Uniform Rapid Suspension process. */
SUSPEND_DOMAIN_URS,
/** Download a list of domains under management. */
DOWNLOAD_DOMAINS,
/** Change the password for a registrar. */
CHANGE_NOMULUS_PASSWORD,
/** View all possible TLDs. */
VIEW_TLD_PORTFOLIO,
/** Onboarding the registrar(s) to extra programs like registry locking. */
ONBOARD_ADDITIONAL_PROGRAMS,
/** Execute arbitrary EPP commands through the UI. */
EXECUTE_EPP_COMMANDS,
/** Send messages to the registry support team. */
CONTACT_SUPPORT,
/** Access billing and payment details for a registrar. */
ACCESS_BILLING_DETAILS,
/** Access any available documentation about the registry. */
ACCESS_DOCUMENTATION,
/** Change the documentation available in the UI about the registry. */
MANAGE_DOCUMENTATION,
/** Viewing the onboarding status of a registrar on a TLD. */
CHECK_ONBOARDING_STATUS,
/** View premium and/or reserved lists for a TLD. */
VIEW_PREMIUM_RESERVED_LISTS,
/** Sign and/or change legal documents like RRAs. */
UPLOAD_CONTRACTS,
/** Viewing legal documents like RRAs. */
ACCESS_CONTRACTS,
/** View analytics on operations and billing data (possibly TBD). */
VIEW_OPERATIONAL_DATA,
/** Create announcements that can be displayed in the UI. */
SEND_ANNOUNCEMENTS,
/** View announcements in the UI. */
VIEW_ANNOUNCEMENTS,
/** Viewing a record of actions performed in the UI for a particular registrar. */
VIEW_ACTIVITY_LOG,
/** View and perform registry locks. */
REGISTRY_LOCK
}
@@ -0,0 +1,112 @@
// Copyright 2022 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.model.console;
import com.google.common.collect.ImmutableSet;
/**
* Definitions of the {@link ConsolePermission} sets that each role contains.
*
* <p>Note: within the "registry" and "registrar" groupings, permissions expand hierarchically,
* where each role has all the permissions of the role below it plus some others.
*/
public class ConsoleRoleDefinitions {
/** Permissions for a registry support agent. */
static final ImmutableSet<ConsolePermission> SUPPORT_AGENT_PERMISSIONS =
ImmutableSet.of(
ConsolePermission.MANAGE_USERS,
ConsolePermission.MANAGE_ACCREDITATION,
ConsolePermission.CONFIGURE_EPP_CONNECTION,
ConsolePermission.DOWNLOAD_DOMAINS,
ConsolePermission.VIEW_TLD_PORTFOLIO,
ConsolePermission.ONBOARD_ADDITIONAL_PROGRAMS,
ConsolePermission.CONTACT_SUPPORT,
ConsolePermission.ACCESS_BILLING_DETAILS,
ConsolePermission.ACCESS_DOCUMENTATION,
ConsolePermission.SUSPEND_DOMAIN_URS,
ConsolePermission.CHECK_ONBOARDING_STATUS,
ConsolePermission.VIEW_PREMIUM_RESERVED_LISTS,
ConsolePermission.UPLOAD_CONTRACTS,
ConsolePermission.ACCESS_CONTRACTS,
ConsolePermission.VIEW_OPERATIONAL_DATA,
ConsolePermission.SEND_ANNOUNCEMENTS,
ConsolePermission.VIEW_ANNOUNCEMENTS,
ConsolePermission.VIEW_ACTIVITY_LOG,
ConsolePermission.REGISTRY_LOCK);
/** Permissions for a registry support lead. */
static final ImmutableSet<ConsolePermission> SUPPORT_LEAD_PERMISSIONS =
new ImmutableSet.Builder<ConsolePermission>()
.addAll(SUPPORT_AGENT_PERMISSIONS)
.add(
ConsolePermission.MANAGE_REGISTRARS,
ConsolePermission.GET_REGISTRANT_EMAIL,
ConsolePermission.SUSPEND_DOMAIN,
ConsolePermission.EXECUTE_EPP_COMMANDS,
ConsolePermission.MANAGE_DOCUMENTATION)
.build();
/** Permissions for a registry full-time employee. */
static final ImmutableSet<ConsolePermission> FTE_PERMISSIONS =
new ImmutableSet.Builder<ConsolePermission>()
.addAll(SUPPORT_LEAD_PERMISSIONS)
.add(ConsolePermission.CHANGE_NOMULUS_PASSWORD)
.build();
/** Permissions for a registrar partner account manager. */
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_PERMISSIONS =
ImmutableSet.of(
ConsolePermission.DOWNLOAD_DOMAINS,
ConsolePermission.VIEW_TLD_PORTFOLIO,
ConsolePermission.CONTACT_SUPPORT,
ConsolePermission.ACCESS_DOCUMENTATION,
ConsolePermission.VIEW_PREMIUM_RESERVED_LISTS,
ConsolePermission.VIEW_OPERATIONAL_DATA,
ConsolePermission.VIEW_ANNOUNCEMENTS);
/** Permissions for a registrar partner account manager that can perform registry locks. */
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS =
new ImmutableSet.Builder<ConsolePermission>()
.addAll(ACCOUNT_MANAGER_PERMISSIONS)
.add(ConsolePermission.REGISTRY_LOCK)
.build();
/** Permissions for the tech contact of a registrar. */
static final ImmutableSet<ConsolePermission> TECH_CONTACT_PERMISSIONS =
new ImmutableSet.Builder<ConsolePermission>()
.addAll(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS)
.add(
ConsolePermission.MANAGE_ACCREDITATION,
ConsolePermission.CONFIGURE_EPP_CONNECTION,
ConsolePermission.CHANGE_NOMULUS_PASSWORD,
ConsolePermission.ONBOARD_ADDITIONAL_PROGRAMS,
ConsolePermission.EXECUTE_EPP_COMMANDS,
ConsolePermission.ACCESS_BILLING_DETAILS,
ConsolePermission.CHECK_ONBOARDING_STATUS,
ConsolePermission.UPLOAD_CONTRACTS,
ConsolePermission.ACCESS_CONTRACTS,
ConsolePermission.VIEW_ACTIVITY_LOG)
.build();
/** Permissions for the primary registrar contact. */
static final ImmutableSet<ConsolePermission> PRIMARY_CONTACT_PERMISSIONS =
new ImmutableSet.Builder<ConsolePermission>()
.addAll(TECH_CONTACT_PERMISSIONS)
.add(ConsolePermission.MANAGE_USERS)
.build();
private ConsoleRoleDefinitions() {}
}
@@ -0,0 +1,44 @@
// Copyright 2022 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.model.console;
import static google.registry.model.console.ConsoleRoleDefinitions.FTE_PERMISSIONS;
import static google.registry.model.console.ConsoleRoleDefinitions.SUPPORT_AGENT_PERMISSIONS;
import static google.registry.model.console.ConsoleRoleDefinitions.SUPPORT_LEAD_PERMISSIONS;
import com.google.common.collect.ImmutableSet;
/** Roles for registry employees that apply across all registrars. */
public enum GlobalRole {
/** The user has no global role, i.e. they're a registrar partner. */
NONE(ImmutableSet.of()),
/** The user is a registry support agent. */
SUPPORT_AGENT(SUPPORT_AGENT_PERMISSIONS),
/** The user is a registry support lead. */
SUPPORT_LEAD(SUPPORT_LEAD_PERMISSIONS),
/** The user is a registry full-time employee. */
FTE(FTE_PERMISSIONS);
private final ImmutableSet<ConsolePermission> permissions;
GlobalRole(ImmutableSet<ConsolePermission> permissions) {
this.permissions = permissions;
}
public boolean hasPermission(ConsolePermission permission) {
return permissions.contains(permission);
}
}
@@ -0,0 +1,45 @@
// Copyright 2022 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.model.console;
import static google.registry.model.console.ConsoleRoleDefinitions.ACCOUNT_MANAGER_PERMISSIONS;
import static google.registry.model.console.ConsoleRoleDefinitions.ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS;
import static google.registry.model.console.ConsoleRoleDefinitions.PRIMARY_CONTACT_PERMISSIONS;
import static google.registry.model.console.ConsoleRoleDefinitions.TECH_CONTACT_PERMISSIONS;
import com.google.common.collect.ImmutableSet;
/** Roles for registrar partners that apply to only one registrar. */
public enum RegistrarRole {
/** The user is a standard account manager at a registrar. */
ACCOUNT_MANAGER(ACCOUNT_MANAGER_PERMISSIONS),
/** The user is an account manager that can perform registry locks. */
ACCOUNT_MANAGER_WITH_REGISTRY_LOCK(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS),
/** The user is a technical contact of a registrar. */
TECH_CONTACT(TECH_CONTACT_PERMISSIONS),
/** The user is the primary contact at a registrar. */
PRIMARY_CONTACT(PRIMARY_CONTACT_PERMISSIONS);
private final ImmutableSet<ConsolePermission> permissions;
RegistrarRole(ImmutableSet<ConsolePermission> permissions) {
this.permissions = permissions;
}
public boolean hasPermission(ConsolePermission permission) {
return permissions.contains(permission);
}
}
@@ -0,0 +1,156 @@
// Copyright 2022 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.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.model.registrar.Registrar.checkValidEmail;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
/** A console user, either a registry employee or a registrar partner. */
public class User extends ImmutableObject implements Buildable {
/** Autogenerated unique ID of this user. */
private long id;
/** GAIA ID associated with the user in question. */
private String gaiaId;
/** Email address of the user in question. */
private String emailAddress;
/** Roles (which grant permissions) associated with this user. */
private UserRoles userRoles;
/**
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
* encoded SHA256 string.
*/
String registryLockPasswordHash;
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
public long getId() {
return id;
}
public String getGaiaId() {
return gaiaId;
}
public String getEmailAddress() {
return emailAddress;
}
public UserRoles getUserRoles() {
return userRoles;
}
public boolean hasRegistryLockPassword() {
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
}
public boolean verifyRegistryLockPassword(String registryLockPassword) {
if (isNullOrEmpty(registryLockPassword)
|| isNullOrEmpty(registryLockPasswordSalt)
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return hashPassword(registryLockPassword, registryLockPasswordSalt)
.equals(registryLockPasswordHash);
}
/**
* Whether the user has the registry lock permission on any registrar or globally.
*
* <p>If so, they should be allowed to (re)set their registry lock password.
*/
public boolean hasAnyRegistryLockPermission() {
if (userRoles == null) {
return false;
}
if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) {
return true;
}
return userRoles.getRegistrarRoles().values().stream()
.anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK));
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Builder for constructing immutable {@link User} objects. */
public static class Builder extends Buildable.Builder<User> {
public Builder() {}
public Builder(User user) {
super(user);
}
@Override
public User build() {
checkArgumentNotNull(getInstance().gaiaId, "Gaia ID cannot be null");
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
return super.build();
}
public Builder setGaiaId(String gaiaId) {
checkArgument(!isNullOrEmpty(gaiaId), "Gaia ID cannot be null or empty");
getInstance().gaiaId = gaiaId;
return this;
}
public Builder setEmailAddress(String emailAddress) {
getInstance().emailAddress = checkValidEmail(emailAddress);
return this;
}
public Builder setUserRoles(UserRoles userRoles) {
checkArgumentNotNull(userRoles, "User roles cannot be null");
getInstance().userRoles = userRoles;
return this;
}
public Builder removeRegistryLockPassword() {
getInstance().registryLockPasswordHash = null;
getInstance().registryLockPasswordSalt = null;
return this;
}
public Builder setRegistryLockPassword(String registryLockPassword) {
checkArgument(
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
checkArgument(
!getInstance().hasRegistryLockPassword(), "User already has a password, remove it first");
checkArgument(
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
getInstance().registryLockPasswordSalt = base64().encode(SALT_SUPPLIER.get());
getInstance().registryLockPasswordHash =
hashPassword(registryLockPassword, getInstance().registryLockPasswordSalt);
return this;
}
}
}
@@ -0,0 +1,120 @@
// Copyright 2022 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.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableMap;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import java.util.Map;
/**
* Contains the global and per-registrar roles for a given user.
*
* <p>See <a href="https://go/nomulus-console-authz">go/nomulus-console-authz</a> for more
* information.
*/
public class UserRoles extends ImmutableObject implements Buildable {
/** Whether the user is a global admin, who has access to everything. */
private boolean isAdmin = false;
/**
* The global role (e.g. {@link GlobalRole#SUPPORT_AGENT}) that the user has across all
* registrars.
*/
private GlobalRole globalRole = GlobalRole.NONE;
/** Any per-registrar roles that this user may have. */
private Map<String, RegistrarRole> registrarRoles = ImmutableMap.of();
/** Whether the user is a global admin, who has access to everything. */
public boolean isAdmin() {
return isAdmin;
}
/**
* The global role (e.g. {@link GlobalRole#SUPPORT_AGENT}) that the user has across all
* registrars.
*/
public GlobalRole getGlobalRole() {
return globalRole;
}
/** Any per-registrar roles that this user may have. */
public Map<String, RegistrarRole> getRegistrarRoles() {
return ImmutableMap.copyOf(registrarRoles);
}
/** If the user has the given permission either globally or on the given registrar. */
public boolean hasPermission(String registrarId, ConsolePermission permission) {
if (hasGlobalPermission(permission)) {
return true;
}
if (!registrarRoles.containsKey(registrarId)) {
return false;
}
return registrarRoles.get(registrarId).hasPermission(permission);
}
/** If the user has the given permission globally across all registrars. */
public boolean hasGlobalPermission(ConsolePermission permission) {
return isAdmin || globalRole.hasPermission(permission);
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Builder for constructing immutable {@link UserRoles} objects. */
public static class Builder extends Buildable.Builder<UserRoles> {
public Builder() {}
private Builder(UserRoles userRoles) {
super(userRoles);
}
@Override
public UserRoles build() {
// Users should only have a global role or per-registrar roles, not both
checkArgument(
getInstance().globalRole.equals(GlobalRole.NONE)
|| getInstance().registrarRoles.isEmpty(),
"Users cannot have both global and per-registrar roles");
return super.build();
}
public Builder setIsAdmin(boolean isAdmin) {
getInstance().isAdmin = isAdmin;
return this;
}
public Builder setGlobalRole(GlobalRole globalRole) {
checkArgumentNotNull(globalRole, "Global role cannot be null");
getInstance().globalRole = globalRole;
return this;
}
public Builder setRegistrarRoles(Map<String, RegistrarRole> registrarRoles) {
checkArgumentNotNull(registrarRoles, "Registrar roles map cannot be null");
getInstance().registrarRoles = ImmutableMap.copyOf(registrarRoles);
return this;
}
}
}
@@ -20,7 +20,7 @@ import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.persistence.WithStringVKey;
import java.util.Set;
@@ -89,14 +89,14 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
})
@Access(AccessType.PROPERTY)
@Column(name = "host_repo_id")
public Set<VKey<HostResource>> getNsHosts() {
return super.nsHosts;
public Set<VKey<Host>> getNsHosts() {
return nsHosts;
}
/**
* Returns the set of {@link GracePeriod} associated with the domain.
*
* <p>This is the getter method specific for Hibernate to access the field so it is set to
* <p>This is the getter method specific for Hibernate to access the field, so it is set to
* private. The caller can use the public {@link #getGracePeriods()} to get the grace periods.
*
* <p>Note that we need to set `insertable = false, updatable = false` for @JoinColumn, otherwise
@@ -104,10 +104,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
* deleting the whole entry from the table when the {@link GracePeriod} is removed from the set.
*/
@Access(AccessType.PROPERTY)
@OneToMany(
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(
name = "domainRepoId",
referencedColumnName = "repoId",
@@ -121,7 +118,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
/**
* Returns the set of {@link DelegationSignerData} associated with the domain.
*
* <p>This is the getter method specific for Hibernate to access the field so it is set to
* <p>This is the getter method specific for Hibernate to access the field, so it is set to
* private. The caller can use the public {@link #getDsData()} to get the DS data.
*
* <p>Note that we need to set `insertable = false, updatable = false` for @JoinColumn, otherwise
@@ -130,10 +127,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
* the set.
*/
@Access(AccessType.PROPERTY)
@OneToMany(
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(
name = "domainRepoId",
referencedColumnName = "repoId",
@@ -182,10 +176,9 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
}
public Builder copyFrom(DomainBase domainBase) {
this.getInstance().copyUpdateTimestamp(domainBase);
return this.setAuthInfo(domainBase.getAuthInfo())
.setAutorenewPollMessage(
domainBase.getAutorenewPollMessage(), domainBase.getAutorenewPollMessageHistoryId())
getInstance().copyUpdateTimestamp(domainBase);
return setAuthInfo(domainBase.getAuthInfo())
.setAutorenewPollMessage(domainBase.getAutorenewPollMessage())
.setAutorenewBillingEvent(domainBase.getAutorenewBillingEvent())
.setAutorenewEndTime(domainBase.getAutorenewEndTime())
.setContacts(domainBase.getContacts())
@@ -210,7 +203,8 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
.setSubordinateHosts(domainBase.getSubordinateHosts())
.setStatusValues(domainBase.getStatusValues())
.setTransferData(domainBase.getTransferData())
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime());
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime())
.setCurrentPackageToken(domainBase.getCurrentPackageToken().orElse(null));
}
}
}
@@ -56,8 +56,9 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
@@ -128,7 +129,7 @@ public class DomainBase extends EppResource
@Index String tld;
/** References to hosts that are the nameservers for the domain. */
@EmptySetToNull @Index @Transient Set<VKey<HostResource>> nsHosts;
@EmptySetToNull @Index @Transient Set<VKey<Host>> nsHosts;
/** Contacts. */
VKey<ContactResource> adminContact;
@@ -215,13 +216,6 @@ public class DomainBase extends EppResource
@Column(name = "autorenew_poll_message_id")
VKey<PollMessage.Autorenew> autorenewPollMessage;
/**
* History record for the autorenew poll message.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Ignore Long autorenewPollMessageHistoryId;
/** The unexpired grace periods for this domain (some of which may not be active yet). */
@Transient Set<GracePeriod> gracePeriods;
@@ -282,6 +276,9 @@ public class DomainBase extends EppResource
// TODO(mcilwain): Start using this field once we are further along in the DB migration.
@Ignore DateTime dnsRefreshRequestTime;
/** The {@link AllocationToken} for the package this domain is currently a part of. */
@Nullable VKey<AllocationToken> currentPackageToken;
/**
* Returns the DNS refresh request time iff this domain's DNS needs refreshing, otherwise absent.
*/
@@ -318,10 +315,6 @@ public class DomainBase extends EppResource
return autorenewPollMessage;
}
public Long getAutorenewPollMessageHistoryId() {
return autorenewPollMessageHistoryId;
}
public ImmutableSet<GracePeriod> getGracePeriods() {
return nullToEmptyImmutableCopy(gracePeriods);
}
@@ -330,6 +323,10 @@ public class DomainBase extends EppResource
return smdId;
}
public Optional<VKey<AllocationToken>> getCurrentPackageToken() {
return Optional.ofNullable(currentPackageToken);
}
/**
* Returns the autorenew end time if there is one, otherwise empty.
*
@@ -372,13 +369,13 @@ public class DomainBase extends EppResource
return idnTableName;
}
public ImmutableSet<VKey<HostResource>> getNameservers() {
public ImmutableSet<VKey<Host>> getNameservers() {
return nullToEmptyImmutableCopy(nsHosts);
}
// Hibernate needs this in order to populate nsHosts but no one else should ever use it
@SuppressWarnings("UnusedMethod")
private void setNsHosts(Set<VKey<HostResource>> nsHosts) {
private void setNsHosts(Set<VKey<Host>> nsHosts) {
this.nsHosts = forceEmptyToNull(nsHosts);
}
@@ -501,9 +498,7 @@ public class DomainBase extends EppResource
// Set the speculatively-written new autorenew events as the domain's autorenew
// events.
.setAutorenewBillingEvent(transferData.getServerApproveAutorenewEvent())
.setAutorenewPollMessage(
transferData.getServerApproveAutorenewPollMessage(),
transferData.getServerApproveAutorenewPollMessageHistoryId());
.setAutorenewPollMessage(transferData.getServerApproveAutorenewPollMessage());
if (transferData.getTransferPeriod().getValue() == 1) {
// Set the grace period using a key to the prescheduled transfer billing event. Not using
// GracePeriod.forBillingEvent() here in order to avoid the actual Datastore fetch.
@@ -602,7 +597,7 @@ public class DomainBase extends EppResource
return tm().transact(
() ->
tm().loadByKeys(getNameservers()).values().stream()
.map(HostResource::getHostName)
.map(Host::getHostName)
.collect(toImmutableSortedSet(Ordering.natural())));
}
@@ -787,32 +782,32 @@ public class DomainBase extends EppResource
return thisCastToDerived();
}
public B setNameservers(VKey<HostResource> nameserver) {
public B setNameservers(VKey<Host> nameserver) {
getInstance().nsHosts = ImmutableSet.of(nameserver);
getInstance().resetUpdateTimestamp();
return thisCastToDerived();
}
public B setNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
public B setNameservers(ImmutableSet<VKey<Host>> nameservers) {
getInstance().nsHosts = forceEmptyToNull(nameservers);
getInstance().resetUpdateTimestamp();
return thisCastToDerived();
}
public B addNameserver(VKey<HostResource> nameserver) {
public B addNameserver(VKey<Host> nameserver) {
return addNameservers(ImmutableSet.of(nameserver));
}
public B addNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
public B addNameservers(ImmutableSet<VKey<Host>> nameservers) {
return setNameservers(
ImmutableSet.copyOf(Sets.union(getInstance().getNameservers(), nameservers)));
}
public B removeNameserver(VKey<HostResource> nameserver) {
public B removeNameserver(VKey<Host> nameserver) {
return removeNameservers(ImmutableSet.of(nameserver));
}
public B removeNameservers(ImmutableSet<VKey<HostResource>> nameservers) {
public B removeNameservers(ImmutableSet<VKey<Host>> nameservers) {
return setNameservers(
ImmutableSet.copyOf(difference(getInstance().getNameservers(), nameservers)));
}
@@ -878,11 +873,8 @@ public class DomainBase extends EppResource
return thisCastToDerived();
}
public B setAutorenewPollMessage(
@Nullable VKey<PollMessage.Autorenew> autorenewPollMessage,
@Nullable Long autorenewPollMessageHistoryId) {
public B setAutorenewPollMessage(@Nullable VKey<PollMessage.Autorenew> autorenewPollMessage) {
getInstance().autorenewPollMessage = autorenewPollMessage;
getInstance().autorenewPollMessageHistoryId = autorenewPollMessageHistoryId;
return thisCastToDerived();
}
@@ -938,5 +930,10 @@ public class DomainBase extends EppResource
getInstance().lastTransferTime = lastTransferTime;
return thisCastToDerived();
}
public B setCurrentPackageToken(@Nullable VKey<AllocationToken> currentPackageToken) {
getInstance().currentPackageToken = currentPackageToken;
return thisCastToDerived();
}
}
}
@@ -38,7 +38,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceCheck;
import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange;
import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.persistence.VKey;
import java.util.Set;
@@ -126,7 +126,7 @@ public class DomainCommand {
Set<String> nameserverFullyQualifiedHostNames;
/** Resolved keys to hosts that are the nameservers for the domain. */
@XmlTransient Set<VKey<HostResource>> nameservers;
@XmlTransient Set<VKey<Host>> nameservers;
/** Foreign keyed associated contacts for the domain (other than registrant). */
@XmlElement(name = "contact")
@@ -156,7 +156,7 @@ public class DomainCommand {
return nullToEmptyImmutableCopy(nameserverFullyQualifiedHostNames);
}
public ImmutableSet<VKey<HostResource>> getNameservers() {
public ImmutableSet<VKey<Host>> getNameservers() {
return nullToEmptyImmutableCopy(nameservers);
}
@@ -349,7 +349,7 @@ public class DomainCommand {
Set<String> nameserverFullyQualifiedHostNames;
/** Resolved keys to hosts that are the nameservers for the domain. */
@XmlTransient Set<VKey<HostResource>> nameservers;
@XmlTransient Set<VKey<Host>> nameservers;
/** Foreign keyed associated contacts for the domain (other than registrant). */
@XmlElement(name = "contact")
@@ -363,7 +363,7 @@ public class DomainCommand {
return nullSafeImmutableCopy(nameserverFullyQualifiedHostNames);
}
public ImmutableSet<VKey<HostResource>> getNameservers() {
public ImmutableSet<VKey<Host>> getNameservers() {
return nullToEmptyImmutableCopy(nameservers);
}
@@ -413,13 +413,13 @@ public class DomainCommand {
}
}
private static Set<VKey<HostResource>> linkHosts(
Set<String> fullyQualifiedHostNames, DateTime now) throws InvalidReferencesException {
private static Set<VKey<Host>> linkHosts(Set<String> fullyQualifiedHostNames, DateTime now)
throws InvalidReferencesException {
if (fullyQualifiedHostNames == null) {
return null;
}
return ImmutableSet.copyOf(
loadByForeignKeysCached(fullyQualifiedHostNames, HostResource.class, now).values());
loadByForeignKeysCached(fullyQualifiedHostNames, Host.class, now).values());
}
private static Set<DesignatedContact> linkContacts(
@@ -26,7 +26,7 @@ import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.secdns.DomainDsDataHistory;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
@@ -113,7 +113,7 @@ public class DomainHistory extends HistoryEntry {
})
@ImmutableObject.EmptySetToNull
@Column(name = "host_repo_id")
Set<VKey<HostResource>> nsHosts;
Set<VKey<Host>> nsHosts;
@DoNotCompare
@OneToMany(
@@ -221,8 +221,8 @@ public class DomainHistory extends HistoryEntry {
return new DomainHistoryId(getDomainRepoId(), getId());
}
/** Returns keys to the {@link HostResource} that are the nameservers for the domain. */
public Set<VKey<HostResource>> getNsHosts() {
/** Returns keys to the {@link Host} that are the nameservers for the domain. */
public Set<VKey<Host>> getNsHosts() {
return nsHosts;
}
@@ -406,7 +406,7 @@ public class DomainHistory extends HistoryEntry {
public DomainHistory buildAndAssemble(
ImmutableSet<DomainDsDataHistory> dsDataHistories,
ImmutableSet<VKey<HostResource>> domainHistoryHosts,
ImmutableSet<VKey<Host>> domainHistoryHosts,
ImmutableSet<GracePeriodHistory> gracePeriodHistories,
ImmutableSet<DomainTransactionRecord> transactionRecords) {
DomainHistory instance = super.build();
@@ -24,8 +24,8 @@ import google.registry.model.contact.ContactBase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.Host;
import google.registry.model.host.HostBase;
import google.registry.model.host.HostResource;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.model.translators.StatusValueAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@@ -135,7 +135,7 @@ public enum StatusValue implements EppEnum {
DomainBase.class,
Domain.class,
HostBase.class,
HostResource.class),
Host.class),
NONE,
DOMAINS(DomainBase.class, Domain.class);
@@ -27,7 +27,7 @@ import javax.persistence.AccessType;
/**
* A persistable Host resource including mutable and non-mutable fields.
*
* <p>The {@link javax.persistence.Id} of the HostResource is the repoId.
* <p>The {@link javax.persistence.Id} of the Host is the repoId.
*/
@ReportedOn
@Entity
@@ -54,7 +54,7 @@ import javax.persistence.AccessType;
@ExternalMessagingName("host")
@WithStringVKey
@Access(AccessType.FIELD) // otherwise it'll use the default if the repoId (property)
public class HostResource extends HostBase implements ForeignKeyedEppResource {
public class Host extends HostBase implements ForeignKeyedEppResource {
@Override
@javax.persistence.Id
@@ -64,8 +64,8 @@ public class HostResource extends HostBase implements ForeignKeyedEppResource {
}
@Override
public VKey<HostResource> createVKey() {
return VKey.create(HostResource.class, getRepoId(), Key.create(this));
public VKey<Host> createVKey() {
return VKey.create(Host.class, getRepoId(), Key.create(this));
}
@Override
@@ -73,11 +73,11 @@ public class HostResource extends HostBase implements ForeignKeyedEppResource {
return new Builder(clone(this));
}
/** A builder for constructing {@link HostResource}, since it is immutable. */
public static class Builder extends HostBase.Builder<HostResource, Builder> {
/** A builder for constructing {@link Host}, since it is immutable. */
public static class Builder extends HostBase.Builder<Host, Builder> {
public Builder() {}
private Builder(HostResource instance) {
private Builder(Host instance) {
super(instance);
}
@@ -126,8 +126,7 @@ public class HostBase extends EppResource {
@Override
public VKey<? extends HostBase> createVKey() {
throw new UnsupportedOperationException(
"HostBase is not an actual persisted entity you can create a key to;"
+ " use HostResource instead");
"HostBase is not an actual persisted entity you can create a key to; use Host instead");
}
@Deprecated
@@ -29,26 +29,26 @@ import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
/** A collection of {@link HostResource} commands. */
/** A collection of {@link Host} commands. */
public class HostCommand {
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5732">RFC5732</a>. */
@XmlTransient
abstract static class HostCreateOrChange extends AbstractSingleResourceCommand
implements ResourceCreateOrChange<HostResource.Builder> {
implements ResourceCreateOrChange<Host.Builder> {
public String getFullyQualifiedHostName() {
return getTargetId();
}
}
/**
* A create command for a {@link HostResource}, mapping "createType" from <a
* A create command for a {@link Host}, mapping "createType" from <a
* href="http://tools.ietf.org/html/rfc5732">RFC5732</a>.
*/
@XmlType(propOrder = {"targetId", "inetAddresses"})
@XmlRootElement
public static class Create extends HostCreateOrChange
implements ResourceCreateOrChange<HostResource.Builder> {
implements ResourceCreateOrChange<Host.Builder> {
/** IP Addresses for this host. Can be null if this is an external host. */
@XmlElement(name = "addr")
Set<InetAddress> inetAddresses;
@@ -58,23 +58,22 @@ public class HostCommand {
}
}
/** A delete command for a {@link HostResource}. */
/** A delete command for a {@link Host}. */
@XmlRootElement
public static class Delete extends AbstractSingleResourceCommand {}
/** An info request for a {@link HostResource}. */
/** An info request for a {@link Host}. */
@XmlRootElement
public static class Info extends AbstractSingleResourceCommand {}
/** A check request for {@link HostResource}. */
/** A check request for {@link Host}. */
@XmlRootElement
public static class Check extends ResourceCheck {}
/** An update to a {@link HostResource}. */
/** An update to a {@link Host}. */
@XmlRootElement
@XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"})
public static class Update extends ResourceUpdate
<Update.AddRemove, HostResource.Builder, Update.Change> {
public static class Update extends ResourceUpdate<Update.AddRemove, Host.Builder, Update.Change> {
@XmlElement(name = "chg")
protected Change innerChange;
@@ -59,7 +59,7 @@ import javax.persistence.PostLoad;
@IdClass(HostHistoryId.class)
public class HostHistory extends HistoryEntry implements UnsafeSerializable {
// Store HostBase instead of HostResource so we don't pick up its @Id
// Store HostBase instead of Host so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
@DoNotCompare @Nullable HostBase hostBase;
@@ -74,7 +74,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
parent = Key.create(HostResource.class, hostRepoId);
parent = Key.create(Host.class, hostRepoId);
}
@Id
@@ -99,9 +99,9 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
return Optional.ofNullable(hostBase);
}
/** The key to the {@link google.registry.model.host.HostResource} this is based off of. */
public VKey<HostResource> getParentVKey() {
return VKey.create(HostResource.class, getHostRepoId());
/** The key to the {@link Host} this is based off of. */
public VKey<Host> getParentVKey() {
return VKey.create(Host.class, getHostRepoId());
}
/** Creates a {@link VKey} instance for this entity. */
@@ -113,7 +113,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
@Override
public Optional<? extends EppResource> getResourceAtPointInTime() {
return getHostBase().map(hostBase -> new HostResource.Builder().copyFrom(hostBase).build());
return getHostBase().map(hostBase -> new Host.Builder().copyFrom(hostBase).build());
}
@PostLoad
@@ -210,7 +210,7 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
}
public Builder setHostRepoId(String hostRepoId) {
getInstance().parent = Key.create(HostResource.class, hostRepoId);
getInstance().parent = Key.create(Host.class, hostRepoId);
return this;
}
}
@@ -49,7 +49,7 @@ import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.persistence.transaction.JpaTransactionManager;
@@ -80,10 +80,10 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
@Entity
public static class ForeignKeyDomainIndex extends ForeignKeyIndex<Domain> {}
/** The {@link ForeignKeyIndex} type for {@link HostResource} entities. */
/** The {@link ForeignKeyIndex} type for {@link Host} entities. */
@ReportedOn
@Entity
public static class ForeignKeyHostIndex extends ForeignKeyIndex<HostResource> {}
public static class ForeignKeyHostIndex extends ForeignKeyIndex<Host> {}
private static final ImmutableBiMap<
Class<? extends EppResource>, Class<? extends ForeignKeyIndex<?>>>
@@ -91,14 +91,14 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
ImmutableBiMap.of(
ContactResource.class, ForeignKeyContactIndex.class,
Domain.class, ForeignKeyDomainIndex.class,
HostResource.class, ForeignKeyHostIndex.class);
Host.class, ForeignKeyHostIndex.class);
private static final ImmutableMap<Class<? extends EppResource>, String>
RESOURCE_CLASS_TO_FKI_PROPERTY =
ImmutableMap.of(
ContactResource.class, "contactId",
Domain.class, "fullyQualifiedDomainName",
HostResource.class, "fullyQualifiedHostName");
Host.class, "fullyQualifiedHostName");
@Id String foreignKey;
@@ -106,7 +106,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
* The deletion time of this {@link ForeignKeyIndex}.
*
* <p>This will generally be equal to the deletion time of {@link #topReference}. However, in the
* case of a {@link HostResource} that was renamed, this field will hold the time of the rename.
* case of a {@link Host} that was renamed, this field will hold the time of the rename.
*/
@Index DateTime deletionTime;
@@ -33,7 +33,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* via Objectify.
*
* <p>All first class entities are represented as a resource class - {@link
* google.registry.model.domain.Domain}, {@link google.registry.model.host.HostResource}, {@link
* google.registry.model.domain.Domain}, {@link google.registry.model.host.Host}, {@link
* google.registry.model.contact.ContactResource}, and {@link
* google.registry.model.registrar.Registrar}. Resource objects are written in a single shared
* entity group per TLD. All commands that operate on those entities are grouped in a "Command"
@@ -20,18 +20,12 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.annotations.OfyIdAllocation;
import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.contact.ContactResource;
@@ -40,9 +34,9 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.DomainRenewData;
import google.registry.model.eppoutput.EppResponse.ResponseData;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.host.HostResource;
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
@@ -55,16 +49,19 @@ import google.registry.persistence.VKey;
import google.registry.persistence.WithLongVKey;
import google.registry.util.NullIgnoringCollectionBuilder;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.PostLoad;
import javax.persistence.Table;
import org.joda.time.DateTime;
/**
@@ -76,10 +73,10 @@ import org.joda.time.DateTime;
* retained for historical purposes, poll messages are truly deleted once they have been delivered
* and ACKed.
*
* <p>Poll messages are parented off of the {@link HistoryEntry} that resulted in their creation.
* This means that poll messages are contained in the Datastore entity group of the parent {@link
* EppResource} (which can be a domain, contact, or host). It is thus possible to perform a strongly
* consistent query to find all poll messages associated with a given EPP resource.
* <p>Poll messages have a reference to the revision id of the {@link HistoryEntry} that resulted in
* their creation or re-creation, in case of transfer cancellation/rejection. The revision ids are
* not used directly by the Java code, but their presence in the database makes it easier to
* diagnose potential issues related to poll messages.
*
* <p>Poll messages are identified externally by registrars using the format defined in {@link
* PollMessageExternalKeyConverter}.
@@ -88,16 +85,10 @@ import org.joda.time.DateTime;
* Command</a>
*/
@Entity
@ReportedOn
@ExternalMessagingName("message")
@javax.persistence.Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
@javax.persistence.Table(
indexes = {
@javax.persistence.Index(columnList = "registrar_id"),
@javax.persistence.Index(columnList = "eventTime")
})
@Table(indexes = {@Index(columnList = "registrar_id"), @Index(columnList = "eventTime")})
public abstract class PollMessage extends ImmutableObject
implements Buildable, TransferServerApproveEntity, UnsafeSerializable {
@@ -105,12 +96,12 @@ public abstract class PollMessage extends ImmutableObject
public enum Type {
DOMAIN(1L, Domain.class),
CONTACT(2L, ContactResource.class),
HOST(3L, HostResource.class);
HOST(3L, Host.class);
private final long id;
private final Class<?> clazz;
private final Class<? extends EppResource> clazz;
Type(long id, Class<?> clazz) {
Type(long id, Class<? extends EppResource> clazz) {
this.id = id;
this.clazz = clazz;
}
@@ -124,58 +115,40 @@ public abstract class PollMessage extends ImmutableObject
}
/** Returns the class of the underlying resource for the poll message type. */
public Class<?> getResourceClass() {
public Class<? extends EppResource> getResourceClass() {
return clazz;
}
/**
* Returns the type specified by the identifer, {@code Optional.empty()} if the id is out of
* range.
*/
public static Optional<Type> fromId(long id) {
for (Type val : values()) {
if (val.id == id) {
return Optional.of(val);
}
}
return Optional.empty();
}
}
/** Entity id. */
@Id
@javax.persistence.Id
@OfyIdAllocation
@Column(name = "poll_message_id")
Long id;
/** The registrar that this poll message will be delivered to. */
@Index
@Column(name = "registrar_id", nullable = false)
String clientId;
/** The time when the poll message should be delivered. May be in the future. */
@Index
@Column(nullable = false)
DateTime eventTime;
/** Human readable message that will be returned with this poll message. */
/** Human-readable message that will be returned with this poll message. */
@Column(name = "message")
String msg;
// TODO(b/456803336): Replace these fields with {Domain,Contact,Host}HistoryId objects.
String domainRepoId;
@Ignore String domainRepoId;
String contactRepoId;
@Ignore String contactRepoId;
String hostRepoId;
@Ignore String hostRepoId;
Long domainHistoryRevisionId;
@Ignore Long domainHistoryRevisionId;
Long contactHistoryRevisionId;
@Ignore Long contactHistoryRevisionId;
@Ignore Long hostHistoryRevisionId;
Long hostHistoryRevisionId;
public Long getId() {
return id;
@@ -216,7 +189,7 @@ public abstract class PollMessage extends ImmutableObject
/**
* Returns the host repo id.
*
* <p>This may only be used on a HostResource poll event.
* <p>This may only be used on a Host poll event.
*/
public String getHostRepoId() {
checkArgument(getType() == Type.DOMAIN);
@@ -228,32 +201,22 @@ public abstract class PollMessage extends ImmutableObject
* the resource.
*/
public String getResourceName() {
return domainRepoId != null
? domainRepoId
: (contactRepoId != null ? contactRepoId : hostRepoId);
return domainRepoId != null ? domainRepoId : contactRepoId != null ? contactRepoId : hostRepoId;
}
/** Gets the underlying history revision id, regardless of the type of the resource. */
public Long getHistoryRevisionId() {
return domainHistoryRevisionId != null
? domainHistoryRevisionId
: (contactHistoryRevisionId != null ? contactHistoryRevisionId : hostHistoryRevisionId);
: contactHistoryRevisionId != null ? contactHistoryRevisionId : hostHistoryRevisionId;
}
public Type getType() {
return domainRepoId != null ? Type.DOMAIN : (contactRepoId != null ? Type.CONTACT : Type.HOST);
return domainRepoId != null ? Type.DOMAIN : contactRepoId != null ? Type.CONTACT : Type.HOST;
}
public abstract ImmutableList<ResponseData> getResponseData();
@Override
public abstract VKey<? extends PollMessage> createVKey();
/** Static VKey factory method for use by VKeyTranslatorFactory. */
public static VKey<PollMessage> createVKey(Key<PollMessage> key) {
return VKey.create(PollMessage.class, key.getId(), key);
}
/** Override Buildable.asBuilder() to give this method stronger typing. */
@Override
public abstract Builder<?, ?> asBuilder();
@@ -318,9 +281,11 @@ public abstract class PollMessage extends ImmutableObject
// Set the appropriate field based on the history entry type.
if (history instanceof DomainHistory) {
return setDomainHistoryId(((DomainHistory) history).getDomainHistoryId());
} else if (history instanceof ContactHistory) {
}
if (history instanceof ContactHistory) {
return setContactHistoryId(((ContactHistory) history).getContactHistoryId());
} else if (history instanceof HostHistory) {
}
if (history instanceof HostHistory) {
return setHostHistoryId(((HostHistory) history).getHostHistoryId());
}
return thisCastToDerived();
@@ -330,7 +295,7 @@ public abstract class PollMessage extends ImmutableObject
* Given an array containing pairs of objects, verifies that both members of exactly one of the
* pairs is non-null.
*/
private boolean exactlyOnePairNonNull(Object... pairs) {
private static boolean exactlyOnePairNonNull(Object... pairs) {
int count = 0;
checkArgument(pairs.length % 2 == 0, "Odd number of arguments provided.");
for (int i = 0; i < pairs.length; i += 2) {
@@ -360,9 +325,9 @@ public abstract class PollMessage extends ImmutableObject
instance.contactHistoryRevisionId,
instance.hostRepoId,
instance.hostHistoryRevisionId),
"the repo id and history revision id must be defined for exactly one of domain, "
+ "contact or host: "
+ instance);
"The repo id and history revision id must be defined for exactly one of domain, "
+ "contact or host: %s",
instance);
return super.build();
}
}
@@ -372,13 +337,11 @@ public abstract class PollMessage extends ImmutableObject
*
* <p>One-time poll messages are deleted from Datastore once they have been delivered and ACKed.
*/
@EntitySubclass(index = false)
@javax.persistence.Entity
@Entity
@DiscriminatorValue("ONE_TIME")
@WithLongVKey(compositeKey = true)
public static class OneTime extends PollMessage {
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(
@@ -399,7 +362,6 @@ public abstract class PollMessage extends ImmutableObject
})
PendingActionNotificationResponse pendingActionNotificationResponse;
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(
@@ -420,35 +382,21 @@ public abstract class PollMessage extends ImmutableObject
})
TransferResponse transferResponse;
@Ignore
@Column(name = "transfer_response_domain_name")
String fullyQualifiedDomainName;
@Ignore
@Column(name = "transfer_response_domain_expiration_time")
DateTime extendedRegistrationExpirationTime;
@Ignore
@Column(name = "transfer_response_contact_id")
String contactId;
@Ignore
@Column(name = "transfer_response_host_id")
String hostId;
@Override
public VKey<OneTime> createVKey() {
return VKey.create(OneTime.class, getId(), Key.create(this));
}
/** Converts an unspecialized VKey&lt;PollMessage&gt; to a VKey of the derived class. */
public static @Nullable VKey<OneTime> convertVKey(@Nullable VKey<? extends PollMessage> key) {
if (key == null) {
return null;
}
Key<OneTime> ofyKey =
Key.create(key.getOfyKey().getParent(), OneTime.class, key.getOfyKey().getId());
return VKey.create(OneTime.class, key.getSqlKey(), ofyKey);
return VKey.createSql(OneTime.class, getId());
}
@Override
@@ -525,8 +473,7 @@ public abstract class PollMessage extends ImmutableObject
/** A builder for {@link OneTime} since it is immutable. */
public static class Builder extends PollMessage.Builder<OneTime, Builder> {
public Builder() {
}
public Builder() {}
private Builder(OneTime instance) {
super(instance);
@@ -587,14 +534,13 @@ public abstract class PollMessage extends ImmutableObject
}
/**
* An auto-renew poll message which recurs annually.
* An autorenew poll message which recurs annually.
*
* <p>Auto-renew poll messages are not deleted until the registration of their parent domain has
* been canceled, because there will always be a speculative renewal for next year until that
* happens.
*/
@EntitySubclass(index = false)
@javax.persistence.Entity
@Entity
@DiscriminatorValue("AUTORENEW")
@WithLongVKey(compositeKey = true)
public static class Autorenew extends PollMessage {
@@ -604,7 +550,6 @@ public abstract class PollMessage extends ImmutableObject
String targetId;
/** The autorenew recurs annually between {@link #eventTime} and this time. */
@Index
DateTime autorenewEndTime;
public String getTargetId() {
@@ -617,25 +562,14 @@ public abstract class PollMessage extends ImmutableObject
@Override
public VKey<Autorenew> createVKey() {
return VKey.create(Autorenew.class, getId(), Key.create(this));
}
/** Converts an unspecialized VKey&lt;PollMessage&gt; to a VKey of the derived class. */
public static @Nullable VKey<Autorenew> convertVKey(VKey<? extends PollMessage> key) {
if (key == null) {
return null;
}
Key<Autorenew> ofyKey =
Key.create(key.getOfyKey().getParent(), Autorenew.class, key.getOfyKey().getId());
return VKey.create(Autorenew.class, key.getSqlKey(), ofyKey);
return VKey.createSql(Autorenew.class, getId());
}
@Override
public ImmutableList<ResponseData> getResponseData() {
// Note that the event time is when the auto-renew occured, so the expiration time in the
// Note that the event time is when the autorenew occurred, so the expiration time in the
// response should be 1 year past that, since it denotes the new expiration time.
return ImmutableList.of(
DomainRenewData.create(getTargetId(), getEventTime().plusYears(1)));
return ImmutableList.of(DomainRenewData.create(getTargetId(), getEventTime().plusYears(1)));
}
@Override
@@ -14,48 +14,38 @@
package google.registry.model.poll;
import com.google.common.base.Splitter;
import com.googlecode.objectify.Key;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import java.util.List;
/**
* A converter between external key strings for {@link PollMessage}s (i.e. what registrars use to
* identify and ACK them) and Datastore keys to the resource.
* identify and ACK them) and {@link VKey}s to the resource.
*
* <p>The format of the key string is A-B-C-D-E-F as follows:
* <p>The format of the key string is A-B as follows:
*
* <pre>
* A = EppResource.typeId (decimal)
* B = EppResource.repoId prefix (STRING)
* C = EppResource.repoId suffix (STRING)
* D = HistoryEntry.id (decimal)
* E = PollMessage.id (decimal)
* F = PollMessage.eventTime (decimal, year only)
* A = PollMessage.id (decimal)
* B = PollMessage.eventTime (decimal, year only)
* </pre>
*
* <p>A typical poll message ID might thus look like: 1-FE0F22-TLD-10071463070-10072612074-2018
* <p>A typical poll message ID might thus look like: 10072612074-2018
*/
public class PollMessageExternalKeyConverter {
public final class PollMessageExternalKeyConverter {
/** An exception thrown when an external key cannot be parsed. */
public static class PollMessageExternalKeyParseException extends RuntimeException {}
public static class PollMessageExternalKeyParseException extends RuntimeException {
private static final long serialVersionUID = 9026397649048831777L;
}
/** Returns an external poll message ID for the given poll message. */
public static String makePollMessageExternalId(PollMessage pollMessage) {
return String.format(
"%d-%s-%d-%d-%d",
pollMessage.getType().getId(),
pollMessage.getResourceName(),
pollMessage.getHistoryRevisionId(),
pollMessage.getId(),
pollMessage.getEventTime().getYear());
return String.format("%d-%d", pollMessage.getId(), pollMessage.getEventTime().getYear());
}
/**
* Returns an Objectify Key to a PollMessage corresponding with the external ID.
* Returns a {@link VKey} to a {@link PollMessage} corresponding with the external ID.
*
* <p>Note that the year field that is included at the end of the poll message isn't actually used
* for anything; it exists solely to create unique externally visible IDs for autorenews. We thus
@@ -66,26 +56,13 @@ public class PollMessageExternalKeyConverter {
*/
public static VKey<PollMessage> parsePollMessageExternalId(String externalKey) {
List<String> idComponents = Splitter.on('-').splitToList(externalKey);
if (idComponents.size() != 6) {
if (idComponents.size() != 2) {
throw new PollMessageExternalKeyParseException();
}
try {
Class<?> resourceClazz =
PollMessage.Type.fromId(Long.parseLong(idComponents.get(0)))
.orElseThrow(() -> new PollMessageExternalKeyParseException())
.getResourceClass();
return VKey.from(
Key.create(
Key.create(
Key.create(
null,
resourceClazz,
String.format("%s-%s", idComponents.get(1), idComponents.get(2))),
HistoryEntry.class,
Long.parseLong(idComponents.get(3))),
PollMessage.class,
Long.parseLong(idComponents.get(4))));
// Note that idComponents.get(5) is entirely ignored; we never use the year field internally.
Long id = Long.parseLong(idComponents.get(0));
return VKey.createSql(PollMessage.class, id);
// Note that idComponents.get(1) is entirely ignored; we never use the year field internally.
} catch (NumberFormatException e) {
throw new PollMessageExternalKeyParseException();
}
@@ -964,7 +964,7 @@ public class Registrar extends ImmutableObject
}
/** Verifies that the email address in question is not null and has a valid format. */
static String checkValidEmail(String email) {
public static String checkValidEmail(String email) {
checkNotNull(email, "Provided email was null");
try {
InternetAddress internetAddress = new InternetAddress(email, true);
@@ -44,10 +44,10 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.host.HostBase;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.host.HostResource;
import google.registry.persistence.VKey;
import java.util.Optional;
import java.util.Set;
@@ -389,7 +389,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
if (parentKind.equals(getKind(Domain.class))) {
resultEntity =
new DomainHistory.Builder().copyFrom(this).setDomainRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(HostResource.class))) {
} else if (parentKind.equals(getKind(Host.class))) {
resultEntity =
new HostHistory.Builder().copyFrom(this).setHostRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(ContactResource.class))) {
@@ -413,7 +413,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
DomainHistory.class,
new DomainHistoryId(repoId, id),
Key.create(parent, DomainHistory.class, id));
} else if (parentKind.equals(getKind(HostResource.class))) {
} else if (parentKind.equals(getKind(Host.class))) {
return VKey.create(
HostHistory.class,
new HostHistoryId(repoId, id),
@@ -29,8 +29,8 @@ import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import java.util.Comparator;
@@ -55,7 +55,7 @@ public class HistoryEntryDao {
ContactHistory.class,
Domain.class,
DomainHistory.class,
HostResource.class,
Host.class,
HostHistory.class);
public static ImmutableMap<Class<? extends HistoryEntry>, String> REPO_ID_FIELD_NAMES =
@@ -73,7 +73,7 @@ public class PremiumListDao {
}
/**
* In-memory price cache for for a given premium list revision and domain label.
* In-memory price cache for a given premium list revision and domain label.
*
* <p>Note that premium list revision ids are globally unique, so this cache is specific to a
* given premium list. Premium list entries might not be present, as indicated by the Optional
@@ -90,12 +90,13 @@ public final class ReservedList
*
* <p>We need to persist the list entries, but only on the initial insert (not on update) since
* the entries themselves never get changed, so we only annotate it with {@link PostPersist}, not
* {@link PostUpdate}.
* PostUpdate.
*/
@PostPersist
void postPersist() {
if (reservedListMap != null) {
reservedListMap.values().stream()
reservedListMap
.values()
.forEach(
entry -> {
// We can safely change the revision id since it's "Insignificant".
@@ -20,7 +20,10 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import google.registry.model.CacheUtils;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import java.util.Map;
@@ -81,9 +84,26 @@ public class ClaimsList extends ImmutableObject {
* <p>This field requires special treatment since we want to lazy load it. We have to remove it
* from the immutability contract so we can modify it after construction and we have to handle the
* database processing on our own so we can detach it after load.
*
* <p>Unlike the cache below, this is guaranteed to contain all mappings from labels to claim keys
* if it is not null.
*/
@Insignificant @Transient ImmutableMap<String, String> labelsToKeys;
/**
* A not-necessarily-complete cache of labels to claim keys.
*
* <p>At any point in time this might or might not contain none, some, or all of the mappings from
* labels to claim keys. Exists so that repeated calls to {@link #getClaimKey(String)} can be
* quick.
*
* <p>Note: this cache has no expiration because while claims list revisions can be added over
* time, each instance of a claims list is immutable.
*/
@Insignificant @Transient @VisibleForTesting
final LoadingCache<String, Optional<String>> claimKeyCache =
CacheUtils.newCacheBuilder().build(this::getClaimKeyUncached);
@PreRemove
void preRemove() {
jpaTm()
@@ -139,18 +159,7 @@ public class ClaimsList extends ImmutableObject {
* entries and cache them locally.
*/
public Optional<String> getClaimKey(String label) {
if (labelsToKeys != null) {
return Optional.ofNullable(labelsToKeys.get(label));
}
return jpaTm()
.transact(
() ->
jpaTm()
.createQueryComposer(ClaimsEntry.class)
.where("revisionId", EQ, revisionId)
.where("domainLabel", EQ, label)
.first()
.map(ClaimsEntry::getClaimKey));
return claimKeyCache.get(label);
}
/**
@@ -192,6 +201,27 @@ public class ClaimsList extends ImmutableObject {
return labelsToKeys.size();
}
/**
* Returns the claim key for a given domain if there is one, empty otherwise.
*
* <p>This attempts to load from the base {@link #labelsToKeys} if possible, otherwise it will
* query the database for the entry requested.
*/
private Optional<String> getClaimKeyUncached(String label) {
if (labelsToKeys != null) {
return Optional.ofNullable(labelsToKeys.get(label));
}
return jpaTm()
.transact(
() ->
jpaTm()
.createQueryComposer(ClaimsEntry.class)
.where("revisionId", EQ, revisionId)
.where("domainLabel", EQ, label)
.first()
.map(ClaimsEntry::getClaimKey));
}
public static ClaimsList create(
DateTime tmdbGenerationTime, ImmutableMap<String, String> labelsToKeys) {
ClaimsList instance = new ClaimsList();
@@ -14,25 +14,57 @@
package google.registry.model.tmch;
import static google.registry.config.RegistryConfig.getClaimsListCacheDuration;
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import google.registry.model.CacheUtils;
import java.time.Duration;
import java.util.Optional;
/** Data access object for {@link ClaimsList}. */
public class ClaimsListDao {
/**
* Cache of the {@link ClaimsList} instance.
*
* <p>The key is meaningless since we only have one active claims list, this is essentially a
* memoizing Supplier that can be reset.
*/
@VisibleForTesting
static LoadingCache<Class<ClaimsListDao>, ClaimsList> CACHE =
createCache(getClaimsListCacheDuration());
@VisibleForTesting
public static void setCacheForTest(Optional<Duration> expiry) {
Duration effectiveExpiry = expiry.orElse(getClaimsListCacheDuration());
CACHE = createCache(effectiveExpiry);
}
private static LoadingCache<Class<ClaimsListDao>, ClaimsList> createCache(Duration expiry) {
return CacheUtils.newCacheBuilder(expiry).build(ignored -> ClaimsListDao.getUncached());
}
/** Saves the given {@link ClaimsList} to Cloud SQL. */
public static void save(ClaimsList claimsList) {
jpaTm().transact(() -> jpaTm().insert(claimsList));
CACHE.put(ClaimsListDao.class, claimsList);
}
/** Returns the most recent revision of the {@link ClaimsList} from the cache. */
public static ClaimsList get() {
return CACHE.get(ClaimsListDao.class);
}
/**
* Returns the most recent revision of the {@link ClaimsList} in SQL or an empty list if it
* doesn't exist.
*/
public static ClaimsList get() {
private static ClaimsList getUncached() {
return jpaTm()
.transact(
() -> {
@@ -37,7 +37,7 @@ import com.google.common.net.InetAddresses;
import com.google.common.primitives.Booleans;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
@@ -301,8 +301,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
*/
private DomainSearchResponse searchByNameserverLdhName(
final RdapSearchPattern partialStringQuery) {
ImmutableCollection<VKey<HostResource>> hostKeys =
getNameserverRefsByLdhName(partialStringQuery);
ImmutableCollection<VKey<Host>> hostKeys = getNameserverRefsByLdhName(partialStringQuery);
if (Iterables.isEmpty(hostKeys)) {
metricInformationBuilder.setNumHostsRetrieved(0);
throw new NotFoundException("No matching nameservers found");
@@ -311,7 +310,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
}
/**
* Assembles a list of {@link HostResource} keys by name.
* Assembles a list of {@link Host} keys by name.
*
* <p>Nameserver query strings with wildcards are allowed to have a suffix after the wildcard,
* which must be a domain. If the domain is not specified, or is not an existing domain in one of
@@ -320,7 +319,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
* initial string is not required (e.g. "*.example.tld" is valid), because we can look up the
* domain and just list all of its subordinate hosts.
*/
private ImmutableCollection<VKey<HostResource>> getNameserverRefsByLdhName(
private ImmutableCollection<VKey<Host>> getNameserverRefsByLdhName(
final RdapSearchPattern partialStringQuery) {
// Handle queries without a wildcard.
if (!partialStringQuery.getHasWildcard()) {
@@ -341,9 +340,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
// limit in order to avoid arbitrarily long-running queries.
Optional<String> desiredRegistrar = getDesiredRegistrar();
if (tm().isOfy()) {
Query<HostResource> query =
Query<Host> query =
queryItems(
HostResource.class,
Host.class,
"fullyQualifiedHostName",
partialStringQuery,
Optional.empty(),
@@ -359,9 +358,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
return replicaJpaTm()
.transact(
() -> {
CriteriaQueryBuilder<HostResource> builder =
CriteriaQueryBuilder<Host> builder =
queryItemsSql(
HostResource.class,
Host.class,
"fullyQualifiedHostName",
partialStringQuery,
Optional.empty(),
@@ -376,22 +375,22 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
return getMatchingResourcesSql(builder, true, maxNameserversInFirstStage)
.resources()
.stream()
.map(HostResource::createVKey)
.map(Host::createVKey)
.collect(toImmutableSet());
});
}
}
/** Assembles a list of {@link HostResource} keys by name when the pattern has no wildcard. */
private ImmutableList<VKey<HostResource>> getNameserverRefsByLdhNameWithoutWildcard(
/** Assembles a list of {@link Host} keys by name when the pattern has no wildcard. */
private ImmutableList<VKey<Host>> getNameserverRefsByLdhNameWithoutWildcard(
final RdapSearchPattern partialStringQuery) {
// If we need to check the sponsoring registrar, we need to load the resource rather than just
// the key.
Optional<String> desiredRegistrar = getDesiredRegistrar();
if (desiredRegistrar.isPresent()) {
Optional<HostResource> host =
Optional<Host> host =
loadByForeignKey(
HostResource.class,
Host.class,
partialStringQuery.getInitialString(),
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
return (!host.isPresent()
@@ -399,17 +398,17 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
? ImmutableList.of()
: ImmutableList.of(host.get().createVKey());
} else {
VKey<HostResource> hostKey =
VKey<Host> hostKey =
loadAndGetKey(
HostResource.class,
Host.class,
partialStringQuery.getInitialString(),
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
return (hostKey == null) ? ImmutableList.of() : ImmutableList.of(hostKey);
}
}
/** Assembles a list of {@link HostResource} keys by name using a superordinate domain suffix. */
private ImmutableList<VKey<HostResource>> getNameserverRefsByLdhNameWithSuffix(
/** Assembles a list of {@link Host} keys by name using a superordinate domain suffix. */
private ImmutableList<VKey<Host>> getNameserverRefsByLdhNameWithSuffix(
final RdapSearchPattern partialStringQuery) {
// The suffix must be a domain that we manage. That way, we can look up the domain and search
// through the subordinate hosts. This is more efficient, and lets us permit wildcard searches
@@ -425,17 +424,15 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
"A suffix in a lookup by nameserver name "
+ "must be a domain defined in the system"));
Optional<String> desiredRegistrar = getDesiredRegistrar();
ImmutableList.Builder<VKey<HostResource>> builder = new ImmutableList.Builder<>();
ImmutableList.Builder<VKey<Host>> builder = new ImmutableList.Builder<>();
for (String fqhn : ImmutableSortedSet.copyOf(domain.getSubordinateHosts())) {
// We can't just check that the host name starts with the initial query string, because
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
if (partialStringQuery.matches(fqhn)) {
if (desiredRegistrar.isPresent()) {
Optional<HostResource> host =
Optional<Host> host =
loadByForeignKey(
HostResource.class,
fqhn,
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
if (host.isPresent()
&& desiredRegistrar
.get()
@@ -443,11 +440,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
builder.add(host.get().createVKey());
}
} else {
VKey<HostResource> hostKey =
VKey<Host> hostKey =
loadAndGetKey(
HostResource.class,
fqhn,
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
if (hostKey != null) {
builder.add(hostKey);
} else {
@@ -477,11 +472,11 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
*/
private DomainSearchResponse searchByNameserverIp(final InetAddress inetAddress) {
Optional<String> desiredRegistrar = getDesiredRegistrar();
ImmutableSet<VKey<HostResource>> hostKeys;
ImmutableSet<VKey<Host>> hostKeys;
if (tm().isOfy()) {
Query<HostResource> query =
Query<Host> query =
queryItems(
HostResource.class,
Host.class,
"inetAddresses",
inetAddress.getHostAddress(),
Optional.empty(),
@@ -524,7 +519,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
@SuppressWarnings("unchecked")
Stream<String> resultStream = query.getResultStream();
return resultStream
.map(repoId -> VKey.create(HostResource.class, repoId))
.map(repoId -> VKey.create(Host.class, repoId))
.collect(toImmutableSet());
});
}
@@ -538,7 +533,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
* #searchByNameserverIp} after they assemble the relevant host keys.
*/
private DomainSearchResponse searchByNameserverRefs(
final ImmutableCollection<VKey<HostResource>> hostKeys) {
final ImmutableCollection<VKey<Host>> hostKeys) {
// We must break the query up into chunks, because the in operator is limited to 30 subqueries.
// Since it is possible for the same domain to show up more than once in our result list (if
// we do a wildcard nameserver search that returns multiple nameservers used by the same
@@ -548,7 +543,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
ImmutableSortedSet.Builder<Domain> domainSetBuilder =
ImmutableSortedSet.orderedBy(Comparator.comparing(Domain::getDomainName));
int numHostKeysSearched = 0;
for (List<VKey<HostResource>> chunk : Iterables.partition(hostKeys, 30)) {
for (List<VKey<Host>> chunk : Iterables.partition(hostKeys, 30)) {
numHostKeysSearched += chunk.size();
if (tm().isOfy()) {
Query<Domain> query =
@@ -574,7 +569,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
replicaJpaTm()
.transact(
() -> {
for (VKey<HostResource> hostKey : hostKeys) {
for (VKey<Host> hostKey : hostKeys) {
CriteriaQueryBuilder<Domain> queryBuilder =
CriteriaQueryBuilder.create(replicaJpaTm(), Domain.class)
.whereFieldContains("nsHosts", hostKey)
@@ -48,7 +48,7 @@ import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.Address;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarPoc;
@@ -235,8 +235,8 @@ public class RdapJsonFormatter {
ImmutableMap.of("type", ImmutableList.of("fax"));
/** Sets the ordering for hosts; just use the fully qualified host name. */
private static final Ordering<HostResource> HOST_RESOURCE_ORDERING =
Ordering.natural().onResultOf(HostResource::getHostName);
private static final Ordering<Host> HOST_RESOURCE_ORDERING =
Ordering.natural().onResultOf(Host::getHostName);
/** Sets the ordering for designated contacts; order them in a fixed order by contact type. */
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
@@ -356,7 +356,7 @@ public class RdapJsonFormatter {
// Kick off the database loads of the nameservers that we will need, so it can load
// asynchronously while we load and process the contacts.
ImmutableSet<HostResource> loadedHosts =
ImmutableSet<Host> loadedHosts =
tm().transact(() -> ImmutableSet.copyOf(tm().loadByKeys(domain.getNameservers()).values()));
// Load the registrant and other contacts and add them to the data.
ImmutableMap<VKey<? extends ContactResource>, ContactResource> loadedContacts =
@@ -393,8 +393,8 @@ public class RdapJsonFormatter {
}
// Add the nameservers to the data; the load was kicked off above for efficiency.
// RDAP Response Profile 2.9: we MUST have the nameservers
for (HostResource hostResource : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
builder.nameserversBuilder().add(createRdapNameserver(hostResource, OutputDataType.INTERNAL));
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
builder.nameserversBuilder().add(createRdapNameserver(host, OutputDataType.INTERNAL));
}
// RDAP Response Profile 2.10 - MUST contain a secureDns member including at least a
@@ -410,23 +410,23 @@ public class RdapJsonFormatter {
}
/**
* Creates a JSON object for a {@link HostResource}.
* Creates a JSON object for a {@link Host}.
*
* @param hostResource the host resource object from which the JSON object should be created
* @param host the host resource object from which the JSON object should be created
* @param outputDataType whether to generate full or summary data
*/
RdapNameserver createRdapNameserver(HostResource hostResource, OutputDataType outputDataType) {
RdapNameserver createRdapNameserver(Host host, OutputDataType outputDataType) {
RdapNameserver.Builder builder = RdapNameserver.builder();
builder.linksBuilder().add(makeSelfLink("nameserver", hostResource.getHostName()));
builder.linksBuilder().add(makeSelfLink("nameserver", host.getHostName()));
if (outputDataType != OutputDataType.FULL) {
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
}
// We need the ldhName: RDAP Response Profile 2.9.1, 4.1
builder.setLdhName(hostResource.getHostName());
builder.setLdhName(host.getHostName());
// Handle is optional, but if given it MUST be the ROID.
// We will set it always as it's important as a "self link"
builder.setHandle(hostResource.getRepoId());
builder.setHandle(host.getRepoId());
// Status is optional for internal Nameservers - RDAP Response Profile 2.9.2
// It isn't mentioned at all anywhere else. So we can just not put it at all?
@@ -434,14 +434,14 @@ public class RdapJsonFormatter {
// To be safe, we'll put it on the "FULL" version anyway
if (outputDataType == OutputDataType.FULL) {
ImmutableSet.Builder<StatusValue> statuses = new ImmutableSet.Builder<>();
statuses.addAll(hostResource.getStatusValues());
if (isLinked(hostResource.createVKey(), getRequestTime())) {
statuses.addAll(host.getStatusValues());
if (isLinked(host.createVKey(), getRequestTime())) {
statuses.add(StatusValue.LINKED);
}
if (hostResource.isSubordinate()
if (host.isSubordinate()
&& tm().transact(
() ->
tm().loadByKey(hostResource.getSuperordinateDomain())
tm().loadByKey(host.getSuperordinateDomain())
.cloneProjectedAtTime(getRequestTime())
.getStatusValues()
.contains(StatusValue.PENDING_TRANSFER))) {
@@ -453,14 +453,14 @@ public class RdapJsonFormatter {
makeStatusValueList(
statuses.build(),
false, // isRedacted
hostResource.getDeletionTime().isBefore(getRequestTime())));
host.getDeletionTime().isBefore(getRequestTime())));
}
// For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2.
//
// However, it is optional for internal responses: RDAP Response Profile 2.9.2
if (outputDataType != OutputDataType.INTERNAL) {
for (InetAddress inetAddress : hostResource.getInetAddresses()) {
for (InetAddress inetAddress : host.getInetAddresses()) {
if (inetAddress instanceof Inet4Address) {
builder.ipv4Builder().add(InetAddresses.toAddrString(inetAddress));
} else if (inetAddress instanceof Inet6Address) {
@@ -472,8 +472,7 @@ public class RdapJsonFormatter {
// RDAP Response Profile 4.3 - Registrar member is optional, so we only set it for FULL
if (outputDataType == OutputDataType.FULL) {
Registrar registrar =
Registrar.loadRequiredRegistrarCached(
hostResource.getPersistedCurrentSponsorRegistrarId());
Registrar.loadRequiredRegistrarCached(host.getPersistedCurrentSponsorRegistrarId());
builder.entitiesBuilder().add(createRdapRegistrarEntity(registrar, OutputDataType.INTERNAL));
}
if (outputDataType != OutputDataType.INTERNAL) {
@@ -21,7 +21,7 @@ import static google.registry.request.Action.Method.HEAD;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import google.registry.flows.EppException;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
@@ -61,12 +61,12 @@ public class RdapNameserverAction extends RdapActionBase {
}
// If there are no undeleted nameservers with the given name, the foreign key should point to
// the most recently deleted one.
Optional<HostResource> hostResource =
Optional<Host> host =
loadByForeignKey(
HostResource.class,
Host.class,
pathSearchString,
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
if (!hostResource.isPresent() || !isAuthorized(hostResource.get())) {
if (!host.isPresent() || !isAuthorized(host.get())) {
// 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.
//
@@ -74,6 +74,6 @@ public class RdapNameserverAction extends RdapActionBase {
// exists but we don't want to show it to you", because we DON'T wish to say that.
throw new NotFoundException(pathSearchString + " not found");
}
return rdapJsonFormatter.createRdapNameserver(hostResource.get(), OutputDataType.FULL);
return rdapJsonFormatter.createRdapNameserver(host.get(), OutputDataType.FULL);
}
}
@@ -28,7 +28,7 @@ import com.google.common.net.InetAddresses;
import com.google.common.primitives.Booleans;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
@@ -159,16 +159,15 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
NameserverSearchResponse.builder()
.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE);
Optional<HostResource> hostResource =
loadByForeignKey(
HostResource.class, partialStringQuery.getInitialString(), getRequestTime());
Optional<Host> host =
loadByForeignKey(Host.class, partialStringQuery.getInitialString(), getRequestTime());
metricInformationBuilder.setNumHostsRetrieved(hostResource.isPresent() ? 1 : 0);
metricInformationBuilder.setNumHostsRetrieved(host.isPresent() ? 1 : 0);
if (shouldBeVisible(hostResource)) {
if (shouldBeVisible(host)) {
builder
.nameserverSearchResultsBuilder()
.add(rdapJsonFormatter.createRdapNameserver(hostResource.get(), OutputDataType.FULL));
.add(rdapJsonFormatter.createRdapNameserver(host.get(), OutputDataType.FULL));
}
return builder.build();
}
@@ -187,7 +186,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
throw new UnprocessableEntityException(
"A suffix after a wildcard in a nameserver lookup must be an in-bailiwick domain");
}
List<HostResource> hostList = new ArrayList<>();
List<Host> hostList = new ArrayList<>();
for (String fqhn : ImmutableSortedSet.copyOf(domain.get().getSubordinateHosts())) {
if (cursorString.isPresent() && (fqhn.compareTo(cursorString.get()) <= 0)) {
continue;
@@ -195,10 +194,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
// We can't just check that the host name starts with the initial query string, because
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
if (partialStringQuery.matches(fqhn)) {
Optional<HostResource> hostResource =
loadByForeignKey(HostResource.class, fqhn, getRequestTime());
if (shouldBeVisible(hostResource)) {
hostList.add(hostResource.get());
Optional<Host> host = loadByForeignKey(Host.class, fqhn, getRequestTime());
if (shouldBeVisible(host)) {
hostList.add(host.get());
if (hostList.size() > rdapResultSetMaxSize) {
break;
}
@@ -222,9 +220,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
// Add 1 so we can detect truncation.
int querySizeLimit = getStandardQuerySizeLimit();
if (tm().isOfy()) {
Query<HostResource> query =
Query<Host> query =
queryItems(
HostResource.class,
Host.class,
"fullyQualifiedHostName",
partialStringQuery,
cursorString,
@@ -236,9 +234,9 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
return replicaJpaTm()
.transact(
() -> {
CriteriaQueryBuilder<HostResource> queryBuilder =
CriteriaQueryBuilder<Host> queryBuilder =
queryItemsSql(
HostResource.class,
Host.class,
"fullyQualifiedHostName",
partialStringQuery,
cursorString,
@@ -254,11 +252,11 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
private NameserverSearchResponse searchByIp(InetAddress inetAddress) {
// Add 1 so we can detect truncation.
int querySizeLimit = getStandardQuerySizeLimit();
RdapResultSet<HostResource> rdapResultSet;
RdapResultSet<Host> rdapResultSet;
if (tm().isOfy()) {
Query<HostResource> query =
Query<Host> query =
queryItems(
HostResource.class,
Host.class,
"inetAddresses",
inetAddress.getHostAddress(),
Optional.empty(),
@@ -296,11 +294,11 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
javax.persistence.Query query =
replicaJpaTm()
.getEntityManager()
.createNativeQuery(queryBuilder.toString(), HostResource.class)
.createNativeQuery(queryBuilder.toString(), Host.class)
.setMaxResults(querySizeLimit);
parameters.build().forEach(query::setParameter);
@SuppressWarnings("unchecked")
List<HostResource> resultList = query.getResultList();
List<Host> resultList = query.getResultList();
return filterResourcesByVisibility(resultList, querySizeLimit);
});
}
@@ -309,7 +307,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
/** Output JSON for a lists of hosts contained in an {@link RdapResultSet}. */
private NameserverSearchResponse makeSearchResults(
RdapResultSet<HostResource> resultSet, CursorType cursorType) {
RdapResultSet<Host> resultSet, CursorType cursorType) {
return makeSearchResults(
resultSet.resources(),
resultSet.incompletenessWarningType(),
@@ -319,7 +317,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
/** Output JSON for a list of hosts. */
private NameserverSearchResponse makeSearchResults(
List<HostResource> hosts,
List<Host> hosts,
IncompletenessWarningType incompletenessWarningType,
int numHostsRetrieved,
CursorType cursorType) {
@@ -329,7 +327,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
NameserverSearchResponse.Builder builder =
NameserverSearchResponse.builder().setIncompletenessWarningType(incompletenessWarningType);
Optional<String> newCursor = Optional.empty();
for (HostResource host : Iterables.limit(hosts, rdapResultSetMaxSize)) {
for (Host host : Iterables.limit(hosts, rdapResultSetMaxSize)) {
newCursor =
Optional.of((cursorType == CursorType.NAME) ? host.getHostName() : host.getRepoId());
builder
@@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.net.InetAddresses;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.xjc.host.XjcHostAddrType;
import google.registry.xjc.host.XjcHostIpType;
import google.registry.xjc.host.XjcHostStatusType;
@@ -30,23 +30,23 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import org.joda.time.DateTime;
/** Utility class that turns {@link HostResource} as {@link XjcRdeHostElement}. */
final class HostResourceToXjcConverter {
/** Utility class that turns a {@link Host} resource into {@link XjcRdeHostElement}. */
final class HostToXjcConverter {
/** Converts a subordinate {@link HostResource} to {@link XjcRdeHostElement}. */
static XjcRdeHostElement convertSubordinate(HostResource host, Domain superordinateDomain) {
/** Converts a subordinate {@link Host} to {@link XjcRdeHostElement}. */
static XjcRdeHostElement convertSubordinate(Host host, Domain superordinateDomain) {
checkArgument(superordinateDomain.createVKey().equals(host.getSuperordinateDomain()));
return new XjcRdeHostElement(convertSubordinateHost(host, superordinateDomain));
}
/** Converts an external {@link HostResource} to {@link XjcRdeHostElement}. */
static XjcRdeHostElement convertExternal(HostResource host) {
/** Converts an external {@link Host} to {@link XjcRdeHostElement}. */
static XjcRdeHostElement convertExternal(Host host) {
checkArgument(!host.isSubordinate());
return new XjcRdeHostElement(convertExternalHost(host));
}
/** Converts {@link HostResource} to {@link XjcRdeHost}. */
static XjcRdeHost convertSubordinateHost(HostResource model, Domain superordinateDomain) {
/** Converts {@link Host} to {@link XjcRdeHost}. */
static XjcRdeHost convertSubordinateHost(Host model, Domain superordinateDomain) {
XjcRdeHost bean =
convertHostCommon(
model,
@@ -58,14 +58,14 @@ final class HostResourceToXjcConverter {
return bean;
}
/** Converts {@link HostResource} to {@link XjcRdeHost}. */
static XjcRdeHost convertExternalHost(HostResource model) {
/** Converts {@link Host} to {@link XjcRdeHost}. */
static XjcRdeHost convertExternalHost(Host model) {
return convertHostCommon(
model, model.getPersistedCurrentSponsorRegistrarId(), model.getLastTransferTime());
}
private static XjcRdeHost convertHostCommon(
HostResource model, String registrarId, DateTime lastTransferTime) {
Host model, String registrarId, DateTime lastTransferTime) {
XjcRdeHost bean = new XjcRdeHost();
bean.setName(model.getHostName());
bean.setRoid(model.getRepoId());
@@ -105,5 +105,5 @@ final class HostResourceToXjcConverter {
return bean;
}
private HostResourceToXjcConverter() {}
private HostToXjcConverter() {}
}
@@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.rde.RdeMode;
import java.util.HashMap;
import java.util.Map;
@@ -71,8 +71,8 @@ public class RdeFragmenter {
cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result);
cache.put(WatermarkModePair.create(watermark, RdeMode.THIN), result);
return result;
} else if (resource instanceof HostResource) {
HostResource host = (HostResource) resource;
} else if (resource instanceof Host) {
Host host = (Host) resource;
result =
Optional.of(
host.isSubordinate()
@@ -22,7 +22,7 @@ import com.googlecode.objectify.Key;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.tldconfig.idn.IdnTable;
@@ -129,16 +129,17 @@ public final class RdeMarshaller implements Serializable {
RdeResourceType.DOMAIN, domain, DomainToXjcConverter.convert(domain, mode));
}
/** Turns {@link HostResource} object into an XML fragment. */
public DepositFragment marshalSubordinateHost(HostResource host, Domain superordinateDomain) {
return marshalResource(RdeResourceType.HOST, host,
HostResourceToXjcConverter.convertSubordinate(host, superordinateDomain));
/** Turns {@link Host} object into an XML fragment. */
public DepositFragment marshalSubordinateHost(Host host, Domain superordinateDomain) {
return marshalResource(
RdeResourceType.HOST,
host,
HostToXjcConverter.convertSubordinate(host, superordinateDomain));
}
/** Turns {@link HostResource} object into an XML fragment. */
public DepositFragment marshalExternalHost(HostResource host) {
return marshalResource(RdeResourceType.HOST, host,
HostResourceToXjcConverter.convertExternal(host));
/** Turns {@link Host} object into an XML fragment. */
public DepositFragment marshalExternalHost(Host host) {
return marshalResource(RdeResourceType.HOST, host, HostToXjcConverter.convertExternal(host));
}
/** Turns {@link Registrar} object into an XML fragment. */
@@ -47,7 +47,7 @@ import google.registry.model.common.Cursor;
import google.registry.model.common.Cursor.CursorType;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
@@ -82,8 +82,8 @@ import org.joda.time.Duration;
* type and loads the embedded resource from it, which is then projected to watermark time to
* account for things like pending transfer.
*
* <p>Only {@link ContactResource}s and {@link HostResource}s that are referenced by an included
* {@link Domain} will be included in the corresponding pending deposit.
* <p>Only {@link ContactResource}s and {@link Host}s that are referenced by an included {@link
* Domain} will be included in the corresponding pending deposit.
*
* <p>{@link Registrar} entities, both active and inactive, are included in all deposits. They are
* not rewinded point-in-time.
@@ -21,7 +21,7 @@ import com.google.common.base.Strings;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.persistence.VKey;
import org.joda.time.DateTime;
@@ -32,7 +32,7 @@ class CommandUtilities {
/** A useful parameter enum for commands that operate on {@link EppResource} objects. */
public enum ResourceType {
CONTACT(ContactResource.class),
HOST(HostResource.class),
HOST(Host.class),
DOMAIN(Domain.class);
private final Class<? extends EppResource> clazz;
@@ -26,7 +26,7 @@ import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.transaction.QueryComposer.Comparator;
import google.registry.tools.params.PathParameter;
import google.registry.util.Clock;
@@ -87,8 +87,8 @@ final class GenerateDnsReportCommand implements CommandWithRemoteApi {
write(domain);
}
Iterable<HostResource> nameservers = tm().transact(() -> tm().loadAllOf(HostResource.class));
for (HostResource nameserver : nameservers) {
Iterable<Host> nameservers = tm().transact(() -> tm().loadAllOf(Host.class));
for (Host nameserver : nameservers) {
// Skip deleted hosts and external hosts.
if (isBeforeOrAt(nameserver.getDeletionTime(), now)
|| nameserver.getInetAddresses().isEmpty()) {
@@ -126,7 +126,7 @@ final class GenerateDnsReportCommand implements CommandWithRemoteApi {
writeJson(mapBuilder.build());
}
private void write(HostResource nameserver) {
private void write(Host nameserver) {
ImmutableList<String> ipAddresses =
nameserver
.getInetAddresses()
@@ -30,8 +30,8 @@ import java.nio.file.Paths;
/**
* A command to download the current claims list.
*
* <p>This is not the original file we fetched from TMCH. It is just a representation of what we
* are currently storing in Datastore.
* <p>This is not the original file we fetched from TMCH. It is just a representation of what we are
* currently storing in SQL.
*/
@Parameters(separators = " =", commandDescription = "Download the current claims list")
final class GetClaimsListCommand implements CommandWithRemoteApi {
@@ -18,7 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.util.DomainNameUtils;
import java.util.List;
@@ -35,7 +35,6 @@ final class GetHostCommand extends GetEppResourceCommand {
public void runAndPrint() {
mainParameters.stream()
.map(DomainNameUtils::canonicalizeHostname)
.forEach(
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
.forEach(h -> printResource("Host", h, loadByForeignKey(Host.class, h, readTimestamp)));
}
}
@@ -17,7 +17,7 @@ package google.registry.tools;
import com.beust.jcommander.Parameters;
import google.registry.tools.server.ListHostsAction;
/** Command to list all HostResource entities in the system. */
/** Command to list all Host entities in the system. */
@Parameters(separators = " =", commandDescription = "List all hosts.")
final class ListHostsCommand extends ListObjectsCommand {
@@ -35,7 +35,7 @@ import com.google.template.soy.data.SoyMapData;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.tools.soy.DomainRenewSoyInfo;
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
import google.registry.util.DomainNameUtils;
@@ -130,7 +130,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName);
Domain domain = domainOpt.get();
Set<String> missingHosts =
difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now));
difference(newHostsSet, checkResourcesExist(Host.class, newCanonicalHosts, now));
checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts);
checkArgument(
locksToPreserve.isEmpty() || undo,
@@ -204,8 +204,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
private ImmutableSortedSet<String> getExistingNameservers(Domain domain) {
ImmutableSortedSet.Builder<String> nameservers = ImmutableSortedSet.naturalOrder();
for (HostResource host :
tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) {
for (Host host : tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) {
nameservers.add(host.getForeignKey());
}
return nameservers.build();
@@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period;
@@ -53,7 +54,7 @@ import org.joda.time.DateTime;
* <p>This removes years off a domain's registration period. Note that the expiration time cannot be
* set to prior than the present. Reversal of the charges for these years (if desired) must happen
* out of band, as they may already have been billed out and thus cannot and won't be reversed in
* Datastore.
* the database.
*/
@Parameters(separators = " =", commandDescription = "Unrenew a domain.")
@NonFinalForTesting
@@ -215,7 +216,9 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
.setHistoryEntry(domainHistory)
.build();
// End the old autorenew billing event and poll message now.
updateAutorenewRecurrenceEndTime(domain, now);
Recurring existingRecurring = tm().loadByKey(domain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime(
domain, existingRecurring, now, domainHistory.getDomainHistoryId());
Domain newDomain =
domain
.asBuilder()
@@ -223,9 +226,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
.setLastEppUpdateTime(now)
.setLastEppUpdateRegistrarId(domain.getCurrentSponsorRegistrarId())
.setAutorenewBillingEvent(newAutorenewEvent.createVKey())
.setAutorenewPollMessage(
newAutorenewPollMessage.createVKey(),
newAutorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(newAutorenewPollMessage.createVKey())
.build();
// In order to do it'll need to write out a new HistoryEntry (likely of type SYNTHETIC), a new
// autorenew billing event and poll message, and a new one time poll message at the present time
@@ -17,13 +17,13 @@ package google.registry.tools.params;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
/** Enum to make it easy for a command to accept a flag that specifies an EppResource subclass. */
public enum EppResourceTypeParameter {
CONTACT(ContactResource.class),
DOMAIN(Domain.class),
HOST(HostResource.class);
HOST(Host.class);
private final Class<? extends EppResource> type;
@@ -31,7 +31,7 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.JsonActionRunner;
@@ -196,8 +196,8 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
Domain domain, DateTime exportTime, ImmutableList.Builder<String> result) {
ImmutableSet<String> subordinateHosts = domain.getSubordinateHosts();
if (!subordinateHosts.isEmpty()) {
for (HostResource unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) {
HostResource host = loadAtPointInTime(unprojectedHost, exportTime);
for (Host unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) {
Host host = loadAtPointInTime(unprojectedHost, exportTime);
// A null means the host was deleted (or not created) at this time.
if (host != null && subordinateHosts.contains(host.getHostName())) {
String stanza = hostStanza(host, domain.getTld());
@@ -232,7 +232,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
private String domainStanza(Domain domain, DateTime exportTime) {
StringBuilder result = new StringBuilder();
String domainLabel = stripTld(domain.getDomainName(), domain.getTld());
for (HostResource nameserver : tm().loadByKeys(domain.getNameservers()).values()) {
for (Host nameserver : tm().loadByKeys(domain.getNameservers()).values()) {
result.append(
String.format(
NS_FORMAT,
@@ -267,7 +267,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
* }
* </pre>
*/
private String hostStanza(HostResource host, String tld) {
private String hostStanza(Host host, String tld) {
StringBuilder result = new StringBuilder();
for (InetAddress addr : host.getInetAddresses()) {
// must be either IPv4 or IPv6
@@ -22,7 +22,7 @@ import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResourceUtils;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -35,7 +35,7 @@ import org.joda.time.DateTime;
path = ListHostsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
public final class ListHostsAction extends ListObjectsAction<HostResource> {
public final class ListHostsAction extends ListObjectsAction<Host> {
public static final String PATH = "/_dr/admin/list/hosts";
@@ -48,10 +48,10 @@ public final class ListHostsAction extends ListObjectsAction<HostResource> {
}
@Override
public ImmutableSet<HostResource> loadObjects() {
public ImmutableSet<Host> loadObjects() {
final DateTime now = clock.nowUtc();
return tm().transact(() -> tm().loadAllOf(HostResource.class)).stream()
return tm().transact(() -> tm().loadAllOf(Host.class)).stream()
.filter(host -> EppResourceUtils.isActive(host, now))
.collect(toImmutableSortedSet(comparing(HostResource::getHostName)));
.collect(toImmutableSortedSet(comparing(Host::getHostName)));
}
}
@@ -18,7 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
import com.google.common.net.InternetDomainName;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -34,10 +34,10 @@ public class NameserverLookupByHostCommand extends DomainOrHostLookupCommand {
@Override
protected Optional<WhoisResponse> getResponse(InternetDomainName hostName, DateTime now) {
Optional<HostResource> hostResource =
Optional<Host> host =
cached
? loadByForeignKeyCached(HostResource.class, hostName.toString(), now)
: loadByForeignKey(HostResource.class, hostName.toString(), now);
return hostResource.map(host -> new NameserverWhoisResponse(host, now));
? loadByForeignKeyCached(Host.class, hostName.toString(), now)
: loadByForeignKey(Host.class, hostName.toString(), now);
return host.map(h -> new NameserverWhoisResponse(h, now));
}
}
@@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.common.net.InetAddresses;
import com.google.common.net.InternetDomainName;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.tld.Registries;
import java.net.InetAddress;
import org.joda.time.DateTime;
@@ -51,12 +51,12 @@ final class NameserverLookupByIpCommand implements WhoisCommand {
@Override
@SuppressWarnings("unchecked")
public WhoisResponse executeQuery(DateTime now) throws WhoisException {
Iterable<HostResource> hostsFromDb;
Iterable<Host> hostsFromDb;
if (tm().isOfy()) {
hostsFromDb =
auditedOfy()
.load()
.type(HostResource.class)
.type(Host.class)
.filter("inetAddresses", ipAddress)
.filter("deletionTime >", now.toDate());
} else {
@@ -76,12 +76,12 @@ final class NameserverLookupByIpCommand implements WhoisCommand {
"SELECT * From \"Host\" WHERE "
+ "ARRAY[ CAST(:address AS TEXT) ] <@ inet_addresses AND "
+ "deletion_time > CAST(:now AS timestamptz)",
HostResource.class)
Host.class)
.setParameter("address", InetAddresses.toAddrString(ipAddress))
.setParameter("now", now.toString())
.getResultList());
}
ImmutableList<HostResource> hosts =
ImmutableList<Host> hosts =
Streams.stream(hostsFromDb)
.filter(
host ->
@@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.InetAddresses;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -32,15 +32,15 @@ import org.joda.time.DateTime;
final class NameserverWhoisResponse extends WhoisResponseImpl {
/** Nameserver(s) which were the target of this WHOIS command. */
private final ImmutableList<HostResource> hosts;
private final ImmutableList<Host> hosts;
/** Creates new WHOIS nameserver response on the given host. */
NameserverWhoisResponse(HostResource host, DateTime timestamp) {
NameserverWhoisResponse(Host host, DateTime timestamp) {
this(ImmutableList.of(checkNotNull(host, "host")), timestamp);
}
/** Creates new WHOIS nameserver response on the given list of hosts. */
NameserverWhoisResponse(ImmutableList<HostResource> hosts, DateTime timestamp) {
NameserverWhoisResponse(ImmutableList<Host> hosts, DateTime timestamp) {
super(timestamp);
this.hosts = checkNotNull(hosts, "hosts");
}
@@ -48,9 +48,9 @@ final class NameserverWhoisResponse extends WhoisResponseImpl {
@Override
public WhoisResponseResults getResponse(boolean preferUnicode, String disclaimer) {
// If we have subordinate hosts, load their registrar ids in a single transaction up-front.
ImmutableList<HostResource> subordinateHosts =
hosts.stream().filter(HostResource::isSubordinate).collect(toImmutableList());
ImmutableMap<HostResource, String> hostRegistrars =
ImmutableList<Host> subordinateHosts =
hosts.stream().filter(Host::isSubordinate).collect(toImmutableList());
ImmutableMap<Host, String> hostRegistrars =
subordinateHosts.isEmpty()
? ImmutableMap.of()
: tm().transact(
@@ -64,7 +64,7 @@ final class NameserverWhoisResponse extends WhoisResponseImpl {
BasicEmitter emitter = new BasicEmitter();
for (int i = 0; i < hosts.size(); i++) {
HostResource host = hosts.get(i);
Host host = hosts.get(i);
String registrarId =
host.isSubordinate()
? hostRegistrars.get(host)
@@ -53,7 +53,7 @@
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
<class>google.registry.model.domain.token.AllocationToken</class>
<class>google.registry.model.host.HostHistory</class>
<class>google.registry.model.host.HostResource</class>
<class>google.registry.model.host.Host</class>
<class>google.registry.model.poll.PollMessage</class>
<class>google.registry.model.poll.PollMessage$OneTime</class>
<class>google.registry.model.poll.PollMessage$Autorenew</class>
@@ -104,7 +104,7 @@
<class>google.registry.model.contact.VKeyConverter_ContactResource</class>
<class>google.registry.model.domain.VKeyConverter_Domain</class>
<class>google.registry.model.domain.token.VKeyConverter_AllocationToken</class>
<class>google.registry.model.host.VKeyConverter_HostResource</class>
<class>google.registry.model.host.VKeyConverter_Host</class>
<class>google.registry.model.poll.VKeyConverter_Autorenew</class>
<class>google.registry.model.poll.VKeyConverter_OneTime</class>
@@ -33,7 +33,6 @@ import google.registry.testing.CloudTasksHelper;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.testing.InjectExtension;
import google.registry.util.CapturingLogHandler;
import google.registry.util.CloudTasksUtils;
import google.registry.util.JdkLoggerConfig;
@@ -57,12 +56,10 @@ public class AsyncTaskEnqueuerTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private AsyncTaskEnqueuer asyncTaskEnqueuer;
private final CapturingLogHandler logHandler = new CapturingLogHandler();
private final FakeClock clock = new FakeClock(DateTime.parse("2015-05-18T12:34:56Z"));
private CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
@BeforeEach
void beforeEach() {
@@ -34,7 +34,6 @@ import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.ofy.Ofy;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.transaction.QueryComposer.Comparator;
@@ -43,7 +42,6 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import java.util.Optional;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -59,14 +57,11 @@ class DeleteExpiredDomainsActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withClock(clock).withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final FakeResponse response = new FakeResponse();
private DeleteExpiredDomainsAction action;
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("tld");
EppController eppController =
DaggerEppTestComponent.builder()
@@ -103,7 +98,7 @@ class DeleteExpiredDomainsActionTest {
// A non-autorenewing domain that is past its expiration time and should be deleted.
// (This is the only one that needs a full set of subsidiary resources, for the delete flow to
// to operate on.)
// operate on.)
Domain pendingExpirationDomain = persistNonAutorenewingDomain("fizz.tld");
assertThat(loadByEntity(pendingExpirationDomain).getStatusValues())
@@ -184,8 +179,7 @@ class DeleteExpiredDomainsActionTest {
.asBuilder()
.setAutorenewEndTime(Optional.of(clock.nowUtc().minusDays(10)))
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
.setAutorenewPollMessage(
autorenewPollMessage.createVKey(), autorenewPollMessage.getHistoryRevisionId())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.build());
return pendingExpirationDomain;
@@ -37,7 +37,7 @@ import com.google.cloud.tasks.v2.HttpMethod;
import com.google.common.collect.ImmutableSet;
import google.registry.model.domain.Domain;
import google.registry.model.domain.RegistryLock;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
@@ -95,7 +95,7 @@ public class RelockDomainActionTest {
@BeforeEach
void beforeEach() throws Exception {
createTlds("tld", "net");
HostResource host = persistActiveHost("ns1.example.net");
Host host = persistActiveHost("ns1.example.net");
domain = persistResource(DatabaseHelper.newDomain(DOMAIN_NAME, host));
oldLock = domainLockUtils.administrativelyApplyLock(DOMAIN_NAME, CLIENT_ID, POC_ID, false);
@@ -20,7 +20,6 @@ import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.newDomain;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
import static google.registry.testing.DatabaseHelper.persistDomainWithPendingTransfer;
@@ -35,14 +34,12 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.ofy.Ofy;
import google.registry.request.Response;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
@@ -62,16 +59,13 @@ public class ResaveEntityActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@Mock private Response response;
private final FakeClock clock = new FakeClock(DateTime.parse("2016-02-11T10:00:00Z"));
private AsyncTaskEnqueuer asyncTaskEnqueuer;
private CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
asyncTaskEnqueuer =
AsyncTaskEnqueuerTest.createForTesting(
cloudTasksHelper.getTestCloudTasksUtils(), clock, Duration.ZERO);
@@ -15,7 +15,6 @@
package google.registry.batch;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.AppEngineExtension.makeRegistrar1;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistResource;
@@ -40,7 +39,6 @@ import google.registry.model.registrar.RegistrarPoc.Type;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import google.registry.util.SelfSignedCaCertificate;
import google.registry.util.SendEmailService;
import java.security.cert.X509Certificate;
@@ -57,11 +55,11 @@ class SendExpiringCertificateNotificationEmailActionTest {
private static final String EXPIRATION_WARNING_EMAIL_BODY_TEXT =
" Dear %1$s,\n"
+ "\n"
+ '\n'
+ "We would like to inform you that your %2$s certificate will expire at %3$s."
+ "\n"
+ '\n'
+ " Kind update your account using the following steps: "
+ "\n"
+ '\n'
+ " 1. Navigate to support and login using your %4$s@registry.example credentials.\n"
+ " 2. Click Settings -> Privacy on the top left corner.\n"
+ " 3. Click Edit and enter certificate string."
@@ -75,7 +73,6 @@ class SendExpiringCertificateNotificationEmailActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final FakeClock clock = new FakeClock(DateTime.parse("2021-05-24T20:21:22Z"));
private final SendEmailService sendEmailService = mock(SendEmailService.class);
private CertificateChecker certificateChecker;
@@ -708,7 +705,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
}
/** Returns persisted sample contacts with a customized contact email type. */
private ImmutableList<RegistrarPoc> persistSampleContacts(
private static ImmutableList<RegistrarPoc> persistSampleContacts(
Registrar registrar, RegistrarPoc.Type emailType) {
return persistSimpleResources(
ImmutableList.of(
@@ -38,7 +38,6 @@ import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -102,7 +101,6 @@ class WipeOutContactHistoryPiiActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final FakeClock clock = new FakeClock(DateTime.parse("2021-08-26T20:21:22Z"));
private FakeResponse response;
@@ -62,9 +62,9 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.host.HostBase;
import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource;
import google.registry.model.rde.RdeMode;
import google.registry.model.rde.RdeRevision;
import google.registry.model.rde.RdeRevision.RdeRevisionId;
@@ -272,7 +272,7 @@ public class RdePipelineTest {
// This host is never referenced.
persistHostHistory(persistActiveHost("ns0.domain.tld"));
HostResource host1 = persistActiveHost("ns1.external.tld");
Host host1 = persistActiveHost("ns1.external.tld");
persistHostHistory(host1);
Domain helloDomain =
persistEppResource(
@@ -282,7 +282,7 @@ public class RdePipelineTest {
.build());
persistDomainHistory(helloDomain);
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
HostResource host2 = persistActiveHost("ns1.hello.soy");
Host host2 = persistActiveHost("ns1.hello.soy");
persistHostHistory(host2);
Domain kittyDomain =
persistEppResource(
@@ -306,7 +306,7 @@ public class RdePipelineTest {
persistContactHistory(contact3);
// This is a subordinate domain in TLD .cat, which is not included in any pending deposit. But
// it should still be included as a subordinate host in the pendign deposit for .soy.
HostResource host3 = persistActiveHost("ns1.lol.cat");
Host host3 = persistActiveHost("ns1.lol.cat");
persistHostHistory(host3);
persistDomainHistory(
helloDomain
@@ -331,7 +331,7 @@ public class RdePipelineTest {
persistDomainHistory(kittyDomain.asBuilder().setDeletionTime(clock.nowUtc()).build());
ContactResource futureContact = persistActiveContact("future-contact");
persistContactHistory(futureContact);
HostResource futureHost = persistActiveHost("ns1.future.tld");
Host futureHost = persistActiveHost("ns1.future.tld");
persistHostHistory(futureHost);
persistDomainHistory(
persistEppResource(
@@ -24,13 +24,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import google.registry.model.ofy.Ofy;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.RequestModule;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.CloudTasksHelper.CloudTasksHelperModule;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.http.HttpServletRequest;
@@ -47,8 +45,6 @@ public final class DnsInjectionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final HttpServletRequest req = mock(HttpServletRequest.class);
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
private final StringWriter httpOutput = new StringWriter();
@@ -58,7 +54,6 @@ public final class DnsInjectionTest {
@BeforeEach
void beforeEach() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
component =
DaggerDnsTestComponent.builder()
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.dns.DnsMetrics.ActionStatus;
@@ -45,7 +46,6 @@ import google.registry.dns.DnsMetrics.CommitStatus;
import google.registry.dns.DnsMetrics.PublishStatus;
import google.registry.dns.writer.DnsWriter;
import google.registry.model.domain.Domain;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.lock.LockHandler;
@@ -55,14 +55,18 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectExtension;
import google.registry.ui.server.SendEmailUtils;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.Optional;
import java.util.Set;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
/** Unit tests for {@link PublishDnsUpdatesAction}. */
public class PublishDnsUpdatesActionTest {
@@ -71,7 +75,6 @@ public class PublishDnsUpdatesActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final FakeClock clock = new FakeClock(DateTime.parse("1971-01-01TZ"));
private final FakeResponse response = new FakeResponse();
private final FakeLockHandler lockHandler = new FakeLockHandler(true);
@@ -80,10 +83,18 @@ public class PublishDnsUpdatesActionTest {
private final DnsQueue dnsQueue = mock(DnsQueue.class);
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
private PublishDnsUpdatesAction action;
private final SendEmailService emailService = mock(SendEmailService.class);
private SendEmailUtils sendEmailUtils;
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
void beforeEach() throws Exception {
sendEmailUtils =
new SendEmailUtils(
new InternetAddress("outgoing@registry.example"),
"UnitTest Registry",
ImmutableList.of("notification@test.example", "notification2@test.example"),
emailService);
createTld("xn--q9jyb4c");
persistResource(
Registry.get("xn--q9jyb4c")
@@ -142,6 +153,10 @@ public class PublishDnsUpdatesActionTest {
hosts,
tld,
Duration.standardSeconds(10),
"Subj",
"Body %1$s %2$s %3$s %4$s %5$s",
"registry@test.com",
"awesomeRegistry",
Optional.ofNullable(retryCount),
Optional.empty(),
dnsQueue,
@@ -150,6 +165,7 @@ public class PublishDnsUpdatesActionTest {
lockHandler,
clock,
cloudTasksHelper.getTestCloudTasksUtils(),
sendEmailUtils,
response);
}
@@ -385,6 +401,37 @@ public class PublishDnsUpdatesActionTest {
assertThat(response.getStatus()).isEqualTo(SC_ACCEPTED);
}
@Test
void testDomainDnsUpdateRetriesExhausted_emailIsSent() {
action =
createAction("xn--q9jyb4c", ImmutableSet.of("example.xn--q9jyb4c"), ImmutableSet.of(), 10);
doThrow(new RuntimeException()).when(dnsWriter).commit();
action.run();
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
verify(emailService).sendEmail(contentCaptor.capture());
EmailMessage emailMessage = contentCaptor.getValue();
assertThat(emailMessage.subject()).isEqualTo("Subj");
assertThat(emailMessage.body())
.isEqualTo(
"Body The Registrar example.xn--q9jyb4c domain awesomeRegistry registry@test.com");
}
@Test
void testHostDnsUpdateRetriesExhausted_emailIsSent() {
action =
createAction(
"xn--q9jyb4c", ImmutableSet.of(), ImmutableSet.of("ns1.example.xn--q9jyb4c"), 10);
doThrow(new RuntimeException()).when(dnsWriter).commit();
action.run();
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
verify(emailService).sendEmail(contentCaptor.capture());
EmailMessage emailMessage = contentCaptor.getValue();
assertThat(emailMessage.subject()).isEqualTo("Subj");
assertThat(emailMessage.body())
.isEqualTo(
"Body The Registrar ns1.example.xn--q9jyb4c host awesomeRegistry registry@test.com");
}
@Test
void testTaskMissingRetryHeaders_throwsException() {
IllegalStateException thrown =
@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newHostResource;
import static google.registry.testing.DatabaseHelper.newHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
@@ -41,7 +41,7 @@ import google.registry.dns.writer.clouddns.CloudDnsWriter.ZoneStateException;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
@@ -283,31 +283,28 @@ public class CloudDnsWriterTest {
/** Returns a domain to be persisted in Datastore. */
private static Domain fakeDomain(
String domainName, ImmutableSet<HostResource> nameservers, int numDsRecords) {
String domainName, ImmutableSet<Host> nameservers, int numDsRecords) {
ImmutableSet.Builder<DelegationSignerData> dsDataBuilder = new ImmutableSet.Builder<>();
for (int i = 0; i < numDsRecords; i++) {
dsDataBuilder.add(DelegationSignerData.create(i, 3, 1, base16().decode("1234567890ABCDEF")));
}
ImmutableSet.Builder<VKey<HostResource>> hostResourceRefBuilder = new ImmutableSet.Builder<>();
for (HostResource nameserver : nameservers) {
hostResourceRefBuilder.add(nameserver.createVKey());
ImmutableSet.Builder<VKey<Host>> hostRefBuilder = new ImmutableSet.Builder<>();
for (Host nameserver : nameservers) {
hostRefBuilder.add(nameserver.createVKey());
}
return DatabaseHelper.newDomain(domainName)
.asBuilder()
.setNameservers(hostResourceRefBuilder.build())
.setNameservers(hostRefBuilder.build())
.setDsData(dsDataBuilder.build())
.build();
}
/** Returns a nameserver used for its NS record. */
private static HostResource fakeHost(String nameserver, InetAddress... addresses) {
return newHostResource(nameserver)
.asBuilder()
.setInetAddresses(ImmutableSet.copyOf(addresses))
.build();
private static Host fakeHost(String nameserver, InetAddress... addresses) {
return newHost(nameserver).asBuilder().setInetAddresses(ImmutableSet.copyOf(addresses)).build();
}
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -18,7 +18,7 @@ import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newHostResource;
import static google.registry.testing.DatabaseHelper.newHost;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
@@ -38,14 +38,13 @@ import com.google.common.net.InetAddresses;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.ofy.Ofy;
import google.registry.model.host.Host;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
@@ -76,8 +75,6 @@ public class DnsUpdateWriterTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withTaskQueue().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@Mock private DnsMessageTransport mockResolver;
@Captor private ArgumentCaptor<Update> updateCaptor;
@@ -87,8 +84,6 @@ public class DnsUpdateWriterTest {
@BeforeEach
void beforeEach() throws Exception {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("tld");
when(mockResolver.send(any(Update.class))).thenReturn(messageWithResponseCode(Rcode.NOERROR));
@@ -98,8 +93,8 @@ public class DnsUpdateWriterTest {
@Test
void testPublishDomainCreate_publishesNameServers() throws Exception {
HostResource host1 = persistActiveHost("ns1.example.tld");
HostResource host2 = persistActiveHost("ns2.example.tld");
Host host1 = persistActiveHost("ns1.example.tld");
Host host2 = persistActiveHost("ns2.example.tld");
Domain domain =
persistActiveDomain("example.tld")
.asBuilder()
@@ -121,7 +116,7 @@ public class DnsUpdateWriterTest {
@MockitoSettings(strictness = Strictness.LENIENT)
@Test
void testPublishAtomic_noCommit() {
HostResource host1 = persistActiveHost("ns.example1.tld");
Host host1 = persistActiveHost("ns.example1.tld");
Domain domain1 =
persistActiveDomain("example1.tld")
.asBuilder()
@@ -129,7 +124,7 @@ public class DnsUpdateWriterTest {
.build();
persistResource(domain1);
HostResource host2 = persistActiveHost("ns.example2.tld");
Host host2 = persistActiveHost("ns.example2.tld");
Domain domain2 =
persistActiveDomain("example2.tld")
.asBuilder()
@@ -145,7 +140,7 @@ public class DnsUpdateWriterTest {
@Test
void testPublishAtomic_oneUpdate() throws Exception {
HostResource host1 = persistActiveHost("ns.example1.tld");
Host host1 = persistActiveHost("ns.example1.tld");
Domain domain1 =
persistActiveDomain("example1.tld")
.asBuilder()
@@ -153,7 +148,7 @@ public class DnsUpdateWriterTest {
.build();
persistResource(domain1);
HostResource host2 = persistActiveHost("ns.example2.tld");
Host host2 = persistActiveHost("ns.example2.tld");
Domain domain2 =
persistActiveDomain("example2.tld")
.asBuilder()
@@ -235,9 +230,9 @@ public class DnsUpdateWriterTest {
@Test
void testPublishHostCreate_publishesAddressRecords() throws Exception {
HostResource host =
Host host =
persistResource(
newHostResource("ns1.example.tld")
newHost("ns1.example.tld")
.asBuilder()
.setInetAddresses(
ImmutableSet.of(
@@ -305,10 +300,10 @@ public class DnsUpdateWriterTest {
@Test
void testPublishDomainExternalAndInBailiwickNameServer() throws Exception {
HostResource externalNameserver = persistResource(newHostResource("ns1.example.com"));
HostResource inBailiwickNameserver =
Host externalNameserver = persistResource(newHost("ns1.example.com"));
Host inBailiwickNameserver =
persistResource(
newHostResource("ns1.example.tld")
newHost("ns1.example.tld")
.asBuilder()
.setInetAddresses(
ImmutableSet.of(
@@ -342,9 +337,9 @@ public class DnsUpdateWriterTest {
@Test
void testPublishDomainDeleteOrphanGlues() throws Exception {
HostResource inBailiwickNameserver =
Host inBailiwickNameserver =
persistResource(
newHostResource("ns1.example.tld")
newHost("ns1.example.tld")
.asBuilder()
.setInetAddresses(
ImmutableSet.of(
@@ -401,7 +396,7 @@ public class DnsUpdateWriterTest {
@SuppressWarnings("AssertThrowsMultipleStatements")
@Test
void testPublishHostFails_whenDnsUpdateReturnsError() throws Exception {
HostResource host =
Host host =
persistActiveSubordinateHost("ns1.example.tld", persistActiveDomain("example.tld"))
.asBuilder()
.setInetAddresses(ImmutableSet.of(InetAddresses.forString("10.0.0.1")))
@@ -418,23 +413,23 @@ public class DnsUpdateWriterTest {
assertThat(thrown).hasMessageThat().contains("SERVFAIL");
}
private void assertThatUpdatedZoneIs(Update update, String zoneName) {
Record[] zoneRecords = update.getSectionArray(Section.ZONE);
assertThat(zoneRecords[0].getName().toString()).isEqualTo(zoneName);
private static void assertThatUpdatedZoneIs(Update update, String zoneName) {
List<Record> zoneRecords = update.getSection(Section.ZONE);
assertThat(zoneRecords.get(0).getName().toString()).isEqualTo(zoneName);
}
private void assertThatTotalUpdateSetsIs(Update update, int count) {
private static void assertThatTotalUpdateSetsIs(Update update, int count) {
assertThat(update.getSectionRRsets(Section.UPDATE)).hasSize(count);
}
private void assertThatUpdateDeletes(Update update, String resourceName, int recordType) {
private static void assertThatUpdateDeletes(Update update, String resourceName, int recordType) {
ImmutableList<Record> deleted = findUpdateRecords(update, resourceName, recordType);
// There's only an empty (i.e. "delete") record.
assertThat(deleted.get(0).rdataToString()).hasLength(0);
assertThat(deleted).hasSize(1);
}
private void assertThatUpdateAdds(
private static void assertThatUpdateAdds(
Update update, String resourceName, int recordType, String... resourceData) {
ArrayList<String> expectedData = new ArrayList<>();
Collections.addAll(expectedData, resourceData);
@@ -446,7 +441,7 @@ public class DnsUpdateWriterTest {
assertThat(actualData).containsExactlyElementsIn(expectedData);
}
private ImmutableList<Record> findUpdateRecords(
private static ImmutableList<Record> findUpdateRecords(
Update update, String resourceName, int recordType) {
for (RRset set : update.getSectionRRsets(Section.UPDATE)) {
if (set.getName().toString().equals(resourceName) && set.getType() == recordType) {
@@ -460,7 +455,7 @@ public class DnsUpdateWriterTest {
throw new AssertionError();
}
private Message messageWithResponseCode(int responseCode) {
private static Message messageWithResponseCode(int responseCode) {
Message message = new Message();
message.getHeader().setOpcode(Opcode.UPDATE);
message.getHeader().setFlag(Flags.QR);
@@ -33,16 +33,13 @@ import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.net.MediaType;
import google.registry.gcs.GcsUtils;
import google.registry.model.ofy.Ofy;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldType;
import google.registry.storage.drive.DriveConnection;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
@@ -51,8 +48,8 @@ import org.mockito.ArgumentCaptor;
class ExportDomainListsActionTest {
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
private DriveConnection driveConnection = mock(DriveConnection.class);
private ArgumentCaptor<byte[]> bytesExportedToDrive = ArgumentCaptor.forClass(byte[].class);
private final DriveConnection driveConnection = mock(DriveConnection.class);
private final ArgumentCaptor<byte[]> bytesExportedToDrive = ArgumentCaptor.forClass(byte[].class);
private ExportDomainListsAction action;
private final FakeClock clock = new FakeClock(DateTime.parse("2020-02-02T02:02:02Z"));
@@ -60,11 +57,6 @@ class ExportDomainListsActionTest {
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withLocalModules().withTaskQueue().build();
@Order(Order.DEFAULT - 1)
@RegisterExtension
public final InjectExtension inject =
new InjectExtension().withStaticFieldOverride(Ofy.class, "clock", clock);
@BeforeEach
void beforeEach() {
createTld("tld");
@@ -42,7 +42,6 @@ import google.registry.request.Response;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.testing.InjectExtension;
import google.registry.util.Retrier;
import java.io.IOException;
import org.junit.jupiter.api.Test;
@@ -59,8 +58,6 @@ public class SyncGroupMembersActionTest {
@RegisterExtension
public final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
private final DirectoryGroupsConnection connection = mock(DirectoryGroupsConnection.class);
private final Response response = mock(Response.class);
@@ -76,8 +73,7 @@ public class SyncGroupMembersActionTest {
@Test
void test_getGroupEmailAddressForContactType_convertsToLowercase() {
assertThat(
getGroupEmailAddressForContactType(
"SomeRegistrar", RegistrarPoc.Type.ADMIN, "domain-registry.example"))
getGroupEmailAddressForContactType("SomeRegistrar", ADMIN, "domain-registry.example"))
.isEqualTo("someregistrar-primary-contacts@domain-registry.example");
}
@@ -35,14 +35,12 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.common.Cursor;
import google.registry.model.ofy.Ofy;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -60,8 +58,6 @@ public class SyncRegistrarsSheetTest {
@RegisterExtension
public final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@Captor private ArgumentCaptor<ImmutableList<ImmutableMap<String, String>>> rowsCaptor;
@Mock private SheetSynchronizer sheetSynchronizer;
@@ -76,7 +72,6 @@ public class SyncRegistrarsSheetTest {
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
createTld("example");
// Remove Registrar entities created by AppEngineExtension (and RegistrarContact's, for jpa).
// We don't do this for ofy because ofy's loadAllOf() can't be called in a transaction but
@@ -215,7 +210,7 @@ public class SyncRegistrarsSheetTest {
+ "Phone number and email visible in domain WHOIS query as "
+ "Registrar Abuse contact info: No\n"
+ "Registrar-Console access: No\n"
+ "\n"
+ '\n'
+ "John Doe\n"
+ "john.doe@example.tld\n"
+ "Tel: +1.1234567890\n"
@@ -95,7 +95,7 @@ class EppLifecycleContactTest extends EppTestCase {
.hasCommandName("PollRequest")
.and()
.hasStatus(SUCCESS_WITH_ACK_MESSAGE);
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "2-1-ROID-3-6-2000"))
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "6-2000"))
.atTime("2000-06-08T22:02:00Z")
.hasResponse("poll_ack_response_empty.xml");
assertThat(getRecordedEppMetric())
@@ -747,11 +747,11 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse(
"poll_response_autorenew.xml",
ImmutableMap.of(
"ID", "1-B-EXAMPLE-12-15-2002",
"ID", "15-2002",
"QDATE", "2002-06-01T00:04:00Z",
"DOMAIN", "fakesite.example",
"EXDATE", "2003-06-01T00:04:00Z"));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-B-EXAMPLE-12-15-2002"))
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "15-2002"))
.atTime("2002-07-01T00:02:00Z")
.hasResponse("poll_ack_response_empty.xml");
@@ -765,13 +765,13 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse(
"poll_response_autorenew.xml",
ImmutableMap.of(
"ID", "1-B-EXAMPLE-12-15-2003", // Note -- Year is different from previous ID.
"ID", "15-2003", // Note -- Year is different from previous ID.
"QDATE", "2003-06-01T00:04:00Z",
"DOMAIN", "fakesite.example",
"EXDATE", "2004-06-01T00:04:00Z"));
// Ack the second poll message and verify that none remain.
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-B-EXAMPLE-12-15-2003"))
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "15-2003"))
.atTime("2003-07-01T00:05:05Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatCommand("poll.xml")
@@ -801,7 +801,7 @@ class EppLifecycleDomainTest extends EppTestCase {
// As the losing registrar, read the request poll message, and then ack it.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
String messageId = "1-B-EXAMPLE-18-24-2001";
String messageId = "24-2001";
assertThatCommand("poll.xml")
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId));
@@ -810,7 +810,7 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse("poll_ack_response_empty.xml");
// Five days in the future, expect a server approval poll message to the loser, and ack it.
messageId = "1-B-EXAMPLE-18-23-2001";
messageId = "23-2001";
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:01:00Z")
.hasResponse(
@@ -822,7 +822,7 @@ class EppLifecycleDomainTest extends EppTestCase {
assertThatLogoutSucceeds();
// Also expect a server approval poll message to the winner, with the transfer request trid.
messageId = "1-B-EXAMPLE-18-22-2001";
messageId = "22-2001";
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:02:00Z")

Some files were not shown because too many files have changed in this diff Show More