mirror of
https://github.com/google/nomulus.git
synced 2025-05-12 22:38:16 +02:00
Flatten the hosts flows
There's so little meat here that there's not much reason to break this cl up any further ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133171754
This commit is contained in:
parent
00ea99960a
commit
516b5663a5
12 changed files with 336 additions and 210 deletions
|
@ -15,14 +15,22 @@
|
|||
package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar;
|
||||
import static google.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static google.registry.model.eppoutput.Result.Code.Success;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.flows.EppException;
|
||||
|
@ -30,10 +38,23 @@ import google.registry.flows.EppException.ObjectAlreadyExistsException;
|
|||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.ResourceUpdateFlow;
|
||||
import google.registry.flows.FlowModule.ClientId;
|
||||
import google.registry.flows.LoggedInFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.async.AsyncFlowUtils;
|
||||
import google.registry.flows.async.DnsRefreshForHostRenameAction;
|
||||
import google.registry.flows.exceptions.AddRemoveSameValueEppException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException;
|
||||
import google.registry.flows.exceptions.StatusNotClientSettableException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.DomainResource;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppinput.ResourceCommand.AddRemoveSameValueException;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.host.HostCommand.Update;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.host.HostResource.Builder;
|
||||
|
@ -47,10 +68,10 @@ import org.joda.time.Duration;
|
|||
* An EPP flow that updates a host resource.
|
||||
*
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link google.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link google.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException}
|
||||
* @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link google.registry.flows.exceptions.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link google.registry.flows.exceptions.StatusNotClientSettableException}
|
||||
* @error {@link HostFlowUtils.HostNameTooShallowException}
|
||||
* @error {@link HostFlowUtils.InvalidHostNameException}
|
||||
* @error {@link HostFlowUtils.SuperordinateDomainDoesNotExistException}
|
||||
|
@ -60,40 +81,115 @@ import org.joda.time.Duration;
|
|||
* @error {@link RenameHostToExternalRemoveIpException}
|
||||
* @error {@link RenameHostToSubordinateRequiresIpException}
|
||||
*/
|
||||
public class HostUpdateFlow extends ResourceUpdateFlow<HostResource, Builder, Update> {
|
||||
public class HostUpdateFlow extends LoggedInFlow implements TransactionalFlow {
|
||||
|
||||
private Key<DomainResource> superordinateDomain;
|
||||
|
||||
private String oldHostName;
|
||||
private String newHostName;
|
||||
private boolean isHostRename;
|
||||
/**
|
||||
* Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it
|
||||
* requires special checking, since you must be able to clear the status off the object with an
|
||||
* update.
|
||||
*/
|
||||
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED);
|
||||
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject Optional<AuthInfo> authInfo;
|
||||
@Inject @ClientId String clientId;
|
||||
@Inject HistoryEntry.Builder historyBuilder;
|
||||
@Inject HostUpdateFlow() {}
|
||||
|
||||
@Override
|
||||
protected void initResourceCreateOrMutateFlow() throws EppException {
|
||||
String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName();
|
||||
isHostRename = suppliedNewHostName != null;
|
||||
oldHostName = targetId;
|
||||
newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
superordinateDomain =
|
||||
lookupSuperordinateDomain(validateHostName(newHostName), now);
|
||||
protected final void initLoggedInFlow() throws EppException {
|
||||
registerExtensions(MetadataExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyUpdateIsAllowed() throws EppException {
|
||||
verifyDomainIsSameRegistrar(superordinateDomain, getClientId());
|
||||
if (isHostRename
|
||||
&& loadAndGetKey(HostResource.class, newHostName, now) != null) {
|
||||
public final EppOutput run() throws EppException {
|
||||
Update command = (Update) resourceCommand;
|
||||
String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName();
|
||||
String targetId = command.getTargetId();
|
||||
HostResource existingResource = loadByUniqueId(HostResource.class, targetId, now);
|
||||
if (existingResource == null) {
|
||||
throw new ResourceToMutateDoesNotExistException(HostResource.class, targetId);
|
||||
}
|
||||
boolean isHostRename = suppliedNewHostName != null;
|
||||
String oldHostName = targetId;
|
||||
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
Optional<DomainResource> superordinateDomain =
|
||||
Optional.fromNullable(lookupSuperordinateDomain(validateHostName(newHostName), now));
|
||||
verifyUpdateAllowed(command, existingResource, superordinateDomain.orNull());
|
||||
if (isHostRename && loadAndGetKey(HostResource.class, newHostName, now) != null) {
|
||||
throw new HostAlreadyExistsException(newHostName);
|
||||
}
|
||||
Builder builder = existingResource.asBuilder();
|
||||
try {
|
||||
command.applyTo(builder);
|
||||
} catch (AddRemoveSameValueException e) {
|
||||
throw new AddRemoveSameValueEppException();
|
||||
}
|
||||
builder
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateClientId(clientId)
|
||||
// The superordinateDomain can be missing if the new name is external.
|
||||
// Note that the value of superordinateDomain is projected to the current time inside of
|
||||
// the lookupSuperordinateDomain(...) call above, so that it will never be stale.
|
||||
.setSuperordinateDomain(
|
||||
superordinateDomain.isPresent() ? Key.create(superordinateDomain.get()) : null)
|
||||
.setLastSuperordinateChange(superordinateDomain == null ? null : now);
|
||||
// Rely on the host's cloneProjectedAtTime() method to handle setting of transfer data.
|
||||
HostResource newResource = builder.build().cloneProjectedAtTime(now);
|
||||
verifyHasIpsIffIsExternal(command, existingResource, newResource);
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newResource);
|
||||
// Keep the {@link ForeignKeyIndex} for this host up to date.
|
||||
if (isHostRename) {
|
||||
// Update the foreign key for the old host name and save one for the new host name.
|
||||
entitiesToSave.add(
|
||||
ForeignKeyIndex.create(existingResource, now),
|
||||
ForeignKeyIndex.create(newResource, newResource.getDeletionTime()));
|
||||
updateSuperordinateDomains(existingResource, newResource);
|
||||
}
|
||||
enqueueTasks(existingResource, newResource);
|
||||
entitiesToSave.add(historyBuilder
|
||||
.setType(HistoryEntry.Type.HOST_UPDATE)
|
||||
.setModificationTime(now)
|
||||
.setParent(Key.create(existingResource))
|
||||
.build());
|
||||
ofy().save().entities(entitiesToSave.build());
|
||||
return createOutput(Success);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyNewUpdatedStateIsAllowed() throws EppException {
|
||||
private void verifyUpdateAllowed(
|
||||
Update command, HostResource existingResource, DomainResource superordinateDomain)
|
||||
throws EppException {
|
||||
verifyOptionalAuthInfoForResource(authInfo, existingResource);
|
||||
if (!isSuperuser) {
|
||||
verifyResourceOwnership(clientId, existingResource);
|
||||
// If the resource is marked with clientUpdateProhibited, and this update does not clear that
|
||||
// status, then the update must be disallowed (unless a superuser is requesting the change).
|
||||
if (!isSuperuser
|
||||
&& existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED)
|
||||
&& !command.getInnerRemove().getStatusValues()
|
||||
.contains(StatusValue.CLIENT_UPDATE_PROHIBITED)) {
|
||||
throw new ResourceHasClientUpdateProhibitedException();
|
||||
}
|
||||
}
|
||||
for (StatusValue statusValue : Sets.union(
|
||||
command.getInnerAdd().getStatusValues(),
|
||||
command.getInnerRemove().getStatusValues())) {
|
||||
if (!isSuperuser && !statusValue.isClientSettable()) { // The superuser can set any status.
|
||||
throw new StatusNotClientSettableException(statusValue.getXmlName());
|
||||
}
|
||||
}
|
||||
verifyDomainIsSameRegistrar(superordinateDomain, clientId);
|
||||
verifyNoDisallowedStatuses(existingResource, DISALLOWED_STATUSES);
|
||||
}
|
||||
|
||||
void verifyHasIpsIffIsExternal(
|
||||
Update command, HostResource existingResource, HostResource newResource) throws EppException {
|
||||
boolean wasExternal = existingResource.getSuperordinateDomain() == null;
|
||||
boolean wasSubordinate = !wasExternal;
|
||||
boolean willBeExternal = superordinateDomain == null;
|
||||
boolean willBeExternal = newResource.getSuperordinateDomain() == null;
|
||||
boolean willBeSubordinate = !willBeExternal;
|
||||
boolean newResourceHasIps = !isNullOrEmpty(newResource.getInetAddresses());
|
||||
boolean commandAddsIps = !isNullOrEmpty(command.getInnerAdd().getInetAddresses());
|
||||
|
@ -114,43 +210,18 @@ public class HostUpdateFlow extends ResourceUpdateFlow<HostResource, Builder, Up
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder setUpdateProperties(Builder builder) {
|
||||
// The superordinateDomain can be null if the new name is external.
|
||||
// Note that the value of superordinateDomain is projected to the current time inside of
|
||||
// the lookupSuperordinateDomain(...) call above, so that it will never be stale.
|
||||
builder.setSuperordinateDomain(superordinateDomain);
|
||||
builder.setLastSuperordinateChange(superordinateDomain == null ? null : now);
|
||||
// Rely on the host's cloneProjectedAtTime() method to handle setting of transfer data.
|
||||
return builder.build().cloneProjectedAtTime(now).asBuilder();
|
||||
}
|
||||
|
||||
/** Keep the {@link ForeignKeyIndex} for this host up to date. */
|
||||
@Override
|
||||
protected void modifyRelatedResources() {
|
||||
if (isHostRename) {
|
||||
// Update the foreign key for the old host name.
|
||||
ofy().save().entity(ForeignKeyIndex.create(existingResource, now));
|
||||
// Save the foreign key for the new host name.
|
||||
ofy().save().entity(ForeignKeyIndex.create(newResource, newResource.getDeletionTime()));
|
||||
updateSuperordinateDomains();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enqueueTasks() throws EppException {
|
||||
DnsQueue dnsQueue = DnsQueue.create();
|
||||
public void enqueueTasks(HostResource existingResource, HostResource newResource) {
|
||||
// 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 (existingResource.getSuperordinateDomain() != null) {
|
||||
dnsQueue.addHostRefreshTask(oldHostName);
|
||||
DnsQueue.create().addHostRefreshTask(existingResource.getFullyQualifiedHostName());
|
||||
}
|
||||
// In case of a rename, there are many updates we need to queue up.
|
||||
if (isHostRename) {
|
||||
if (((Update) resourceCommand).getInnerChange().getFullyQualifiedHostName() != null) {
|
||||
// If the renamed host is also subordinate, then we must enqueue an update to write the new
|
||||
// glue.
|
||||
if (newResource.getSuperordinateDomain() != null) {
|
||||
dnsQueue.addHostRefreshTask(newHostName);
|
||||
DnsQueue.create().addHostRefreshTask(newResource.getFullyQualifiedHostName());
|
||||
}
|
||||
// We must also enqueue updates for all domains that use this host as their nameserver so
|
||||
// that their NS records can be updated to point at the new name.
|
||||
|
@ -163,33 +234,29 @@ public class HostUpdateFlow extends ResourceUpdateFlow<HostResource, Builder, Up
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.HOST_UPDATE;
|
||||
}
|
||||
|
||||
private void updateSuperordinateDomains() {
|
||||
private void updateSuperordinateDomains(HostResource existingResource, HostResource newResource) {
|
||||
Key<DomainResource> oldSuperordinateDomain = existingResource.getSuperordinateDomain();
|
||||
if (oldSuperordinateDomain != null || superordinateDomain != null) {
|
||||
if (Objects.equals(oldSuperordinateDomain, superordinateDomain)) {
|
||||
Key<DomainResource> newSuperordinateDomain = newResource.getSuperordinateDomain();
|
||||
if (oldSuperordinateDomain != null || newSuperordinateDomain != null) {
|
||||
if (Objects.equals(oldSuperordinateDomain, newSuperordinateDomain)) {
|
||||
ofy().save().entity(
|
||||
ofy().load().key(oldSuperordinateDomain).now().asBuilder()
|
||||
.removeSubordinateHost(oldHostName)
|
||||
.addSubordinateHost(newHostName)
|
||||
.removeSubordinateHost(existingResource.getFullyQualifiedHostName())
|
||||
.addSubordinateHost(newResource.getFullyQualifiedHostName())
|
||||
.build());
|
||||
} else {
|
||||
if (oldSuperordinateDomain != null) {
|
||||
ofy().save().entity(
|
||||
ofy().load().key(oldSuperordinateDomain).now()
|
||||
.asBuilder()
|
||||
.removeSubordinateHost(oldHostName)
|
||||
.removeSubordinateHost(existingResource.getFullyQualifiedHostName())
|
||||
.build());
|
||||
}
|
||||
if (superordinateDomain != null) {
|
||||
if (newSuperordinateDomain != null) {
|
||||
ofy().save().entity(
|
||||
ofy().load().key(superordinateDomain).now()
|
||||
ofy().load().key(newSuperordinateDomain).now()
|
||||
.asBuilder()
|
||||
.addSubordinateHost(newHostName)
|
||||
.addSubordinateHost(newResource.getFullyQualifiedHostName())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue