mirror of
https://github.com/google/nomulus.git
synced 2025-07-09 12:43:24 +02:00
Refactor lock/unlock commands to use Registry Locks (#390)
* Refactor lock/unlock commands to use Registry Locks * CR responses * Remove unnecessary XML test files * Add tests * Respond to CR * Refactor further the creation and verification of locks * remove isUnlocked * Responses to CR * Fix tests * Add admin-override back to unlocking * Add TODO * Fix imports
This commit is contained in:
parent
02e7106262
commit
7df3d85243
16 changed files with 907 additions and 224 deletions
|
@ -30,7 +30,7 @@ public final class RegistryLockDao {
|
|||
* code (there may be two instances of the same code in the database--one after lock object
|
||||
* creation and one after verification.
|
||||
*/
|
||||
public static RegistryLock getByVerificationCode(String verificationCode) {
|
||||
public static Optional<RegistryLock> getByVerificationCode(String verificationCode) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
|
@ -42,9 +42,8 @@ public final class RegistryLockDao {
|
|||
Long.class)
|
||||
.setParameter("verificationCode", verificationCode)
|
||||
.getSingleResult();
|
||||
// TODO(gbrodman): Don't throw NPE here. Maybe NoResultException fits better?
|
||||
checkNotNull(revisionId, "No registry lock with this code");
|
||||
return em.find(RegistryLock.class, revisionId);
|
||||
return Optional.ofNullable(revisionId)
|
||||
.map(revision -> em.find(RegistryLock.class, revision));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,8 +66,8 @@ public final class RegistryLockDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent lock object for a given repo ID (i.e. a domain) or empty if this domain
|
||||
* hasn't been locked before.
|
||||
* Returns the most recent lock object for a given domain specified by repo ID, or empty if this
|
||||
* domain hasn't been locked before.
|
||||
*/
|
||||
public static Optional<RegistryLock> getMostRecentByRepoId(String repoId) {
|
||||
return jpaTm()
|
||||
|
@ -86,6 +85,28 @@ public final class RegistryLockDao {
|
|||
.findFirst());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent verified lock object for a given domain specified by repo ID, or empty
|
||||
* if no lock has ever been finalized for this domain. This is different from {@link
|
||||
* #getMostRecentByRepoId(String)} in that it only returns verified locks.
|
||||
*/
|
||||
public static Optional<RegistryLock> getMostRecentVerifiedLockByRepoId(String repoId) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(
|
||||
"SELECT lock FROM RegistryLock lock WHERE lock.repoId = :repoId AND"
|
||||
+ " lock.lockCompletionTimestamp IS NOT NULL ORDER BY lock.revisionId"
|
||||
+ " DESC",
|
||||
RegistryLock.class)
|
||||
.setParameter("repoId", repoId)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
public static RegistryLock save(RegistryLock registryLock) {
|
||||
checkNotNull(registryLock, "Null registry lock cannot be saved");
|
||||
return jpaTm().transact(() -> jpaTm().getEntityManager().merge(registryLock));
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package google.registry.schema.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.toZonedDateTime;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
|
@ -22,6 +23,7 @@ import google.registry.model.Buildable;
|
|||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
|
@ -182,6 +184,20 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
|||
return lockCompletionTimestamp != null && unlockCompletionTimestamp == null;
|
||||
}
|
||||
|
||||
/** Returns true iff the lock was requested >= 1 hour ago and has not been verified. */
|
||||
public boolean isLockRequestExpired(Clock clock) {
|
||||
return !getLockCompletionTimestamp().isPresent()
|
||||
&& isBeforeOrAt(getLockRequestTimestamp(), clock.nowUtc().minusHours(1));
|
||||
}
|
||||
|
||||
/** Returns true iff the unlock was requested >= 1 hour ago and has not been verified. */
|
||||
public boolean isUnlockRequestExpired(Clock clock) {
|
||||
Optional<DateTime> unlockRequestTimestamp = getUnlockRequestTimestamp();
|
||||
return unlockRequestTimestamp.isPresent()
|
||||
&& !getUnlockCompletionTimestamp().isPresent()
|
||||
&& isBeforeOrAt(unlockRequestTimestamp.get(), clock.nowUtc().minusHours(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
|
|
269
core/src/main/java/google/registry/tools/DomainLockUtils.java
Normal file
269
core/src/main/java/google/registry/tools/DomainLockUtils.java
Normal file
|
@ -0,0 +1,269 @@
|
|||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utility class for validating and applying {@link RegistryLock}s.
|
||||
*
|
||||
* <p>For both locks and unlocks, a lock must be requested via the createRegistry*Requst methods
|
||||
* then verified through the verifyAndApply* methods. These methods will verify that the domain in
|
||||
* question is in a lock/unlockable state and will return the lock object.
|
||||
*/
|
||||
public final class DomainLockUtils {
|
||||
|
||||
private DomainLockUtils() {}
|
||||
|
||||
public static RegistryLock createRegistryLockRequest(
|
||||
String domainName,
|
||||
String registrarId,
|
||||
@Nullable String registrarPocId,
|
||||
boolean isAdmin,
|
||||
Clock clock) {
|
||||
DomainBase domainBase = getDomain(domainName, clock);
|
||||
verifyDomainNotLocked(domainBase);
|
||||
|
||||
// Multiple pending actions are not allowed
|
||||
RegistryLockDao.getMostRecentByRepoId(domainBase.getRepoId())
|
||||
.ifPresent(
|
||||
previousLock ->
|
||||
checkArgument(
|
||||
previousLock.isLockRequestExpired(clock)
|
||||
|| previousLock.getUnlockCompletionTimestamp().isPresent(),
|
||||
"A pending or completed lock action already exists for %s",
|
||||
previousLock.getDomainName()));
|
||||
|
||||
RegistryLock lock =
|
||||
new RegistryLock.Builder()
|
||||
.setVerificationCode(UUID.randomUUID().toString())
|
||||
.setDomainName(domainName)
|
||||
.setRepoId(domainBase.getRepoId())
|
||||
.setRegistrarId(registrarId)
|
||||
.setRegistrarPocId(registrarPocId)
|
||||
.isSuperuser(isAdmin)
|
||||
.build();
|
||||
return RegistryLockDao.save(lock);
|
||||
}
|
||||
|
||||
public static RegistryLock createRegistryUnlockRequest(
|
||||
String domainName, String registrarId, boolean isAdmin, Clock clock) {
|
||||
DomainBase domainBase = getDomain(domainName, clock);
|
||||
Optional<RegistryLock> lockOptional =
|
||||
RegistryLockDao.getMostRecentVerifiedLockByRepoId(domainBase.getRepoId());
|
||||
|
||||
RegistryLock.Builder newLockBuilder;
|
||||
if (isAdmin) {
|
||||
// Admins should always be able to unlock domains in case we get in a bad state
|
||||
// TODO(b/147411297): Remove the admin checks / failsafes once we have migrated existing
|
||||
// locked domains to have lock objects
|
||||
newLockBuilder =
|
||||
lockOptional
|
||||
.map(RegistryLock::asBuilder)
|
||||
.orElse(
|
||||
new RegistryLock.Builder()
|
||||
.setRepoId(domainBase.getRepoId())
|
||||
.setDomainName(domainName)
|
||||
.setLockCompletionTimestamp(clock.nowUtc())
|
||||
.setRegistrarId(registrarId));
|
||||
} else {
|
||||
verifyDomainLocked(domainBase);
|
||||
RegistryLock lock =
|
||||
lockOptional.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("No lock object for domain %s", domainName)));
|
||||
checkArgument(
|
||||
lock.isLocked(), "Lock object for domain %s is not currently locked", domainName);
|
||||
checkArgument(
|
||||
!lock.getUnlockRequestTimestamp().isPresent() || lock.isUnlockRequestExpired(clock),
|
||||
"A pending unlock action already exists for %s",
|
||||
domainName);
|
||||
checkArgument(
|
||||
lock.getRegistrarId().equals(registrarId),
|
||||
"Lock object does not have registrar ID %s",
|
||||
registrarId);
|
||||
checkArgument(
|
||||
!lock.isSuperuser(), "Non-admin user cannot unlock admin-locked domain %s", domainName);
|
||||
newLockBuilder = lock.asBuilder();
|
||||
}
|
||||
RegistryLock newLock =
|
||||
newLockBuilder
|
||||
.setVerificationCode(UUID.randomUUID().toString())
|
||||
.isSuperuser(isAdmin)
|
||||
.setUnlockRequestTimestamp(clock.nowUtc())
|
||||
.setRegistrarId(registrarId)
|
||||
.build();
|
||||
return RegistryLockDao.save(newLock);
|
||||
}
|
||||
|
||||
public static RegistryLock verifyAndApplyLock(
|
||||
String verificationCode, boolean isAdmin, Clock clock) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
|
||||
checkArgument(
|
||||
!lock.getLockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already locked",
|
||||
lock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!lock.isLockRequestExpired(clock),
|
||||
"The pending lock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin lock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
lock.asBuilder().setLockCompletionTimestamp(clock.nowUtc()).build());
|
||||
tm().transact(() -> applyLockStatuses(newLock, clock));
|
||||
return newLock;
|
||||
});
|
||||
}
|
||||
|
||||
public static RegistryLock verifyAndApplyUnlock(
|
||||
String verificationCode, boolean isAdmin, Clock clock) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
RegistryLock lock = getByVerificationCode(verificationCode);
|
||||
checkArgument(
|
||||
!lock.getUnlockCompletionTimestamp().isPresent(),
|
||||
"Domain %s is already unlocked",
|
||||
lock.getDomainName());
|
||||
|
||||
checkArgument(
|
||||
!lock.isUnlockRequestExpired(clock),
|
||||
"The pending unlock has expired; please try again");
|
||||
|
||||
checkArgument(
|
||||
isAdmin || !lock.isSuperuser(), "Non-admin user cannot complete admin unlock");
|
||||
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
lock.asBuilder().setUnlockCompletionTimestamp(clock.nowUtc()).build());
|
||||
tm().transact(() -> removeLockStatuses(newLock, isAdmin, clock));
|
||||
return newLock;
|
||||
});
|
||||
}
|
||||
|
||||
private static void verifyDomainNotLocked(DomainBase domainBase) {
|
||||
checkArgument(
|
||||
!domainBase.getStatusValues().containsAll(REGISTRY_LOCK_STATUSES),
|
||||
"Domain %s is already locked",
|
||||
domainBase.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
private static void verifyDomainLocked(DomainBase domainBase) {
|
||||
checkArgument(
|
||||
!Sets.intersection(domainBase.getStatusValues(), REGISTRY_LOCK_STATUSES).isEmpty(),
|
||||
"Domain %s is already unlocked",
|
||||
domainBase.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
private static DomainBase getDomain(String domainName, Clock clock) {
|
||||
return loadByForeignKeyCached(DomainBase.class, domainName, clock.nowUtc())
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException(String.format("Unknown domain %s", domainName)));
|
||||
}
|
||||
|
||||
private static RegistryLock getByVerificationCode(String verificationCode) {
|
||||
return RegistryLockDao.getByVerificationCode(verificationCode)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Invalid verification code %s", verificationCode)));
|
||||
}
|
||||
|
||||
private static void applyLockStatuses(RegistryLock lock, Clock clock) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), clock);
|
||||
verifyDomainNotLocked(domain);
|
||||
|
||||
DomainBase newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
.setStatusValues(
|
||||
ImmutableSet.copyOf(Sets.union(domain.getStatusValues(), REGISTRY_LOCK_STATUSES)))
|
||||
.build();
|
||||
saveEntities(newDomain, lock, clock);
|
||||
}
|
||||
|
||||
private static void removeLockStatuses(RegistryLock lock, boolean isAdmin, Clock clock) {
|
||||
DomainBase domain = getDomain(lock.getDomainName(), clock);
|
||||
if (!isAdmin) {
|
||||
verifyDomainLocked(domain);
|
||||
}
|
||||
|
||||
DomainBase newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
.setStatusValues(
|
||||
ImmutableSet.copyOf(
|
||||
Sets.difference(domain.getStatusValues(), REGISTRY_LOCK_STATUSES)))
|
||||
.build();
|
||||
saveEntities(newDomain, lock, clock);
|
||||
}
|
||||
|
||||
private static void saveEntities(DomainBase domain, RegistryLock lock, Clock clock) {
|
||||
String reason = "Lock or unlock of a domain through a RegistryLock operation";
|
||||
HistoryEntry historyEntry =
|
||||
new HistoryEntry.Builder()
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setBySuperuser(lock.isSuperuser())
|
||||
.setRequestedByRegistrar(!lock.isSuperuser())
|
||||
.setType(HistoryEntry.Type.DOMAIN_UPDATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setParent(Key.create(domain))
|
||||
.setReason(reason)
|
||||
.build();
|
||||
ofy().save().entities(domain, historyEntry);
|
||||
if (!lock.isSuperuser()) { // admin actions shouldn't affect billing
|
||||
BillingEvent.OneTime oneTime =
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.SERVER_STATUS)
|
||||
.setTargetId(domain.getForeignKey())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setCost(Registry.get(domain.getTld()).getServerStatusChangeCost())
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setBillingTime(clock.nowUtc())
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(oneTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,25 +14,19 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.tools.soy.DomainUpdateSoyInfo;
|
||||
import java.util.Optional;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A command to registry lock domain names via EPP.
|
||||
* A command to registry lock domain names.
|
||||
*
|
||||
* <p>A registry lock consists of server-side statuses preventing deletes, updates, and transfers.
|
||||
*/
|
||||
|
@ -42,41 +36,35 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
|
|||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
protected ImmutableSet<String> getRelevantDomains() {
|
||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime now = clock.nowUtc();
|
||||
ImmutableSet.Builder<String> relevantDomains = new ImmutableSet.Builder<>();
|
||||
for (String domain : getDomains()) {
|
||||
Optional<DomainBase> domainBase = loadByForeignKey(DomainBase.class, domain, now);
|
||||
checkArgumentPresent(domainBase, "Domain '%s' does not exist or is deleted", domain);
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToAdd =
|
||||
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.get().getStatusValues())
|
||||
.immutableCopy();
|
||||
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.getStatusValues()).immutableCopy();
|
||||
if (statusesToAdd.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already locked and needs no updates.", domain);
|
||||
continue;
|
||||
}
|
||||
relevantDomains.add(domain);
|
||||
}
|
||||
return relevantDomains.build();
|
||||
}
|
||||
|
||||
setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE);
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"domain", domain,
|
||||
"add", true,
|
||||
"addNameservers", ImmutableList.of(),
|
||||
"addAdmins", ImmutableList.of(),
|
||||
"addTechs", ImmutableList.of(),
|
||||
"addStatuses",
|
||||
statusesToAdd.stream().map(StatusValue::getXmlName).collect(toImmutableList()),
|
||||
"remove", false,
|
||||
"removeNameservers", ImmutableList.of(),
|
||||
"removeAdmins", ImmutableList.of(),
|
||||
"removeTechs", ImmutableList.of(),
|
||||
"removeStatuses", ImmutableList.of(),
|
||||
"change", false,
|
||||
"secdns", false,
|
||||
"addDsRecords", ImmutableList.of(),
|
||||
"removeDsRecords", ImmutableList.of(),
|
||||
"removeAllDsRecords", false));
|
||||
}
|
||||
@Override
|
||||
protected RegistryLock createLock(String domain) {
|
||||
return DomainLockUtils.createRegistryLockRequest(domain, clientId, null, true, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,16 +22,23 @@ import static google.registry.util.CollectionUtils.findDuplicates;
|
|||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Shared base class for commands to registry lock or unlock a domain via EPP. */
|
||||
public abstract class LockOrUnlockDomainCommand extends MutatingEppToolCommand {
|
||||
public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
|
||||
implements CommandWithCloudSql {
|
||||
|
||||
protected static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
|
||||
ImmutableSet.of(
|
||||
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
|
||||
|
||||
|
@ -48,23 +55,52 @@ public abstract class LockOrUnlockDomainCommand extends MutatingEppToolCommand {
|
|||
@Config("registryAdminClientId")
|
||||
String registryAdminClientId;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
protected ImmutableSet<String> relevantDomains = ImmutableSet.of();
|
||||
|
||||
protected ImmutableSet<String> getDomains() {
|
||||
return ImmutableSet.copyOf(mainParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initEppToolCommand() throws Exception {
|
||||
// Superuser status is required to update registry lock statuses.
|
||||
superuser = true;
|
||||
|
||||
protected void init() {
|
||||
// Default clientId to the registry registrar account if otherwise unspecified.
|
||||
if (clientId == null) {
|
||||
clientId = registryAdminClientId;
|
||||
}
|
||||
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
|
||||
checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates);
|
||||
initMutatingEppToolCommand();
|
||||
System.out.println(
|
||||
"== ENSURE THAT YOU HAVE AUTHENTICATED THE REGISTRAR BEFORE RUNNING THIS COMMAND ==");
|
||||
relevantDomains = getRelevantDomains();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() {
|
||||
int failures = 0;
|
||||
for (String domain : relevantDomains) {
|
||||
try {
|
||||
RegistryLock lock = createLock(domain);
|
||||
finalizeLockOrUnlockRequest(lock);
|
||||
} catch (Throwable t) {
|
||||
Throwable rootCause = Throwables.getRootCause(t);
|
||||
logger.atSevere().withCause(rootCause).log("Error when (un)locking domain %s.", domain);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
if (failures == 0) {
|
||||
return String.format("Successfully locked/unlocked %d domains.", relevantDomains.size());
|
||||
} else {
|
||||
return String.format(
|
||||
"Successfully locked/unlocked %d domains with %d failures.",
|
||||
relevantDomains.size() - failures, failures);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ImmutableSet<String> getRelevantDomains();
|
||||
|
||||
protected abstract RegistryLock createLock(String domain);
|
||||
|
||||
protected abstract void finalizeLockOrUnlockRequest(RegistryLock lock);
|
||||
}
|
||||
|
|
|
@ -14,25 +14,19 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.tools.soy.DomainUpdateSoyInfo;
|
||||
import java.util.Optional;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A command to registry unlock domain names via EPP.
|
||||
* A command to registry unlock domain names.
|
||||
*
|
||||
* <p>A registry lock consists of server-side statuses preventing deletes, updates, and transfers.
|
||||
*/
|
||||
|
@ -42,41 +36,35 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
|
|||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
protected ImmutableSet<String> getRelevantDomains() {
|
||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime now = clock.nowUtc();
|
||||
ImmutableSet.Builder<String> relevantDomains = new ImmutableSet.Builder<>();
|
||||
for (String domain : getDomains()) {
|
||||
Optional<DomainBase> domainBase = loadByForeignKey(DomainBase.class, domain, now);
|
||||
checkArgumentPresent(domainBase, "Domain '%s' does not exist or is deleted", domain);
|
||||
DomainBase domainBase =
|
||||
loadByForeignKey(DomainBase.class, domain, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||
ImmutableSet<StatusValue> statusesToRemove =
|
||||
Sets.intersection(domainBase.get().getStatusValues(), REGISTRY_LOCK_STATUSES)
|
||||
.immutableCopy();
|
||||
Sets.intersection(domainBase.getStatusValues(), REGISTRY_LOCK_STATUSES).immutableCopy();
|
||||
if (statusesToRemove.isEmpty()) {
|
||||
logger.atInfo().log("Domain '%s' is already unlocked and needs no updates.", domain);
|
||||
continue;
|
||||
}
|
||||
relevantDomains.add(domain);
|
||||
}
|
||||
return relevantDomains.build();
|
||||
}
|
||||
|
||||
setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE);
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"domain", domain,
|
||||
"add", false,
|
||||
"addNameservers", ImmutableList.of(),
|
||||
"addAdmins", ImmutableList.of(),
|
||||
"addTechs", ImmutableList.of(),
|
||||
"addStatuses", ImmutableList.of(),
|
||||
"remove", true,
|
||||
"removeNameservers", ImmutableList.of(),
|
||||
"removeAdmins", ImmutableList.of(),
|
||||
"removeTechs", ImmutableList.of(),
|
||||
"removeStatuses",
|
||||
statusesToRemove.stream().map(StatusValue::getXmlName).collect(toImmutableList()),
|
||||
"change", false,
|
||||
"secdns", false,
|
||||
"addDsRecords", ImmutableList.of(),
|
||||
"removeDsRecords", ImmutableList.of(),
|
||||
"removeAllDsRecords", false));
|
||||
}
|
||||
@Override
|
||||
protected RegistryLock createLock(String domain) {
|
||||
return DomainLockUtils.createRegistryUnlockRequest(domain, clientId, true, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||
DomainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), true, clock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,23 +44,13 @@ public final class RegistryLockDaoTest {
|
|||
public void testSaveAndLoad_success() {
|
||||
RegistryLock lock = createLock();
|
||||
RegistryLockDao.save(lock);
|
||||
RegistryLock fromDatabase = RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
||||
RegistryLock fromDatabase =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(fromDatabase.getDomainName()).isEqualTo(lock.getDomainName());
|
||||
assertThat(fromDatabase.getVerificationCode()).isEqualTo(lock.getVerificationCode());
|
||||
assertThat(fromDatabase.getLastUpdateTimestamp()).isEqualTo(jpaRule.getTxnClock().nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndLoad_failure_differentCode() {
|
||||
RegistryLock lock = createLock();
|
||||
RegistryLockDao.save(lock);
|
||||
NullPointerException thrown =
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> RegistryLockDao.getByVerificationCode(UUID.randomUUID().toString()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("No registry lock with this code");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveTwiceAndLoad_returnsLatest() {
|
||||
RegistryLock lock = createLock();
|
||||
|
@ -70,7 +60,7 @@ public final class RegistryLockDaoTest {
|
|||
.transact(
|
||||
() -> {
|
||||
RegistryLock updatedLock =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
RegistryLockDao.save(
|
||||
updatedLock
|
||||
.asBuilder()
|
||||
|
@ -81,7 +71,7 @@ public final class RegistryLockDaoTest {
|
|||
.transact(
|
||||
() -> {
|
||||
RegistryLock fromDatabase =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(fromDatabase.getLockCompletionTimestamp().get())
|
||||
.isEqualTo(jpaRule.getTxnClock().nowUtc());
|
||||
assertThat(fromDatabase.getLastUpdateTimestamp())
|
||||
|
@ -100,7 +90,8 @@ public final class RegistryLockDaoTest {
|
|||
.setUnlockCompletionTimestamp(jpaRule.getTxnClock().nowUtc())
|
||||
.build());
|
||||
RegistryLockDao.save(lock);
|
||||
RegistryLock fromDatabase = RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
||||
RegistryLock fromDatabase =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(fromDatabase.getUnlockRequestTimestamp())
|
||||
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
||||
assertThat(fromDatabase.getUnlockCompletionTimestamp())
|
||||
|
@ -119,7 +110,7 @@ public final class RegistryLockDaoTest {
|
|||
.transact(
|
||||
() -> {
|
||||
RegistryLock fromDatabase =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(fromDatabase.getLockCompletionTimestamp())
|
||||
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
||||
});
|
||||
|
@ -130,6 +121,11 @@ public final class RegistryLockDaoTest {
|
|||
assertThrows(NullPointerException.class, () -> RegistryLockDao.save(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLock_unknownCode() {
|
||||
assertThat(RegistryLockDao.getByVerificationCode("hi").isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoad_lockedDomains_byRegistrarId() {
|
||||
RegistryLock lock =
|
||||
|
@ -180,6 +176,28 @@ public final class RegistryLockDaoTest {
|
|||
assertThat(RegistryLockDao.getMostRecentByRepoId("nonexistent").isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoad_verified_byRepoId() {
|
||||
RegistryLock completedLock =
|
||||
createLock().asBuilder().setLockCompletionTimestamp(jpaRule.getTxnClock().nowUtc()).build();
|
||||
RegistryLockDao.save(completedLock);
|
||||
|
||||
jpaRule.getTxnClock().advanceOneMilli();
|
||||
RegistryLock inProgressLock = createLock();
|
||||
RegistryLockDao.save(inProgressLock);
|
||||
|
||||
Optional<RegistryLock> mostRecent = RegistryLockDao.getMostRecentVerifiedLockByRepoId("repoId");
|
||||
assertThat(mostRecent.isPresent()).isTrue();
|
||||
assertThat(mostRecent.get().isLocked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoad_verified_byRepoId_empty() {
|
||||
RegistryLockDao.save(createLock());
|
||||
Optional<RegistryLock> mostRecent = RegistryLockDao.getMostRecentVerifiedLockByRepoId("repoId");
|
||||
assertThat(mostRecent.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
private RegistryLock createLock() {
|
||||
return new RegistryLock.Builder()
|
||||
.setRepoId("repoId")
|
||||
|
|
|
@ -22,6 +22,9 @@ import google.registry.schema.tld.PremiumListUtilsTest;
|
|||
import google.registry.schema.tld.ReservedListDaoTest;
|
||||
import google.registry.schema.tmch.ClaimsListDaoTest;
|
||||
import google.registry.tools.CreateReservedListCommandTest;
|
||||
import google.registry.tools.DomainLockUtilsTest;
|
||||
import google.registry.tools.LockDomainCommandTest;
|
||||
import google.registry.tools.UnlockDomainCommandTest;
|
||||
import google.registry.tools.UpdateReservedListCommandTest;
|
||||
import google.registry.tools.server.CreatePremiumListActionTest;
|
||||
import google.registry.tools.server.UpdatePremiumListActionTest;
|
||||
|
@ -42,14 +45,17 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
ClaimsListDaoTest.class,
|
||||
CreatePremiumListActionTest.class,
|
||||
CreateReservedListCommandTest.class,
|
||||
CursorDaoTest.class,
|
||||
CreatePremiumListActionTest.class,
|
||||
DomainLockUtilsTest.class,
|
||||
LockDomainCommandTest.class,
|
||||
PremiumListDaoTest.class,
|
||||
PremiumListUtilsTest.class,
|
||||
RegistryLockDaoTest.class,
|
||||
RegistryLockGetActionTest.class,
|
||||
ReservedListDaoTest.class,
|
||||
UnlockDomainCommandTest.class,
|
||||
UpdatePremiumListActionTest.class,
|
||||
UpdateReservedListCommandTest.class
|
||||
})
|
||||
|
|
|
@ -948,7 +948,7 @@ public class DatastoreHelper {
|
|||
* Returns all of the history entries that are parented off the given EppResource with the given
|
||||
* type.
|
||||
*/
|
||||
public static List<HistoryEntry> getHistoryEntriesOfType(
|
||||
public static ImmutableList<HistoryEntry> getHistoryEntriesOfType(
|
||||
EppResource resource, final HistoryEntry.Type type) {
|
||||
return getHistoryEntries(resource)
|
||||
.stream()
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.testing.DatastoreHelper.createTlds;
|
||||
import static google.registry.testing.DatastoreHelper.getHistoryEntriesOfType;
|
||||
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatastoreHelper.newDomainBase;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationTestRule;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.DatastoreHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.UserInfo;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link google.registry.tools.DomainLockUtils}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public final class DomainLockUtilsTest {
|
||||
|
||||
private static final String DOMAIN_NAME = "example.tld";
|
||||
private static final String POC_ID = "marla.singer@example.com";
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngineRule =
|
||||
AppEngineRule.builder()
|
||||
.withDatastore()
|
||||
.withUserService(UserInfo.create(POC_ID, "12345"))
|
||||
.build();
|
||||
|
||||
@Rule
|
||||
public final JpaIntegrationTestRule jpaRule =
|
||||
new JpaTestRules.Builder().buildIntegrationTestRule();
|
||||
|
||||
private final FakeClock clock = jpaRule.getTxnClock();
|
||||
|
||||
private DomainBase domain;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
createTlds("tld", "net");
|
||||
HostResource host = persistActiveHost("ns1.example.net");
|
||||
domain = persistResource(newDomainBase(DOMAIN_NAME, host));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_createLock() {
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_createUnlock() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_createUnlock_adminUnlockingAdmin() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", true, clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_createLock_previousLockExpired() {
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_applyLockDomain() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
assertThat(reloadDomain().getStatusValues()).containsExactlyElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
|
||||
assertThat(historyEntry.getBySuperuser()).isFalse();
|
||||
assertThat(historyEntry.getReason())
|
||||
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
|
||||
assertBillingEvent(historyEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_applyUnlockDomain() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
RegistryLock unlock =
|
||||
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
|
||||
DomainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
|
||||
|
||||
assertThat(reloadDomain().getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
ImmutableList<HistoryEntry> historyEntries =
|
||||
getHistoryEntriesOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertThat(historyEntries.size()).isEqualTo(2);
|
||||
historyEntries.forEach(
|
||||
entry -> {
|
||||
assertThat(entry.getRequestedByRegistrar()).isTrue();
|
||||
assertThat(entry.getBySuperuser()).isFalse();
|
||||
assertThat(entry.getReason())
|
||||
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
|
||||
});
|
||||
assertBillingEvents(historyEntries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_applyAdminLock_onlyHistoryEntry() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||
|
||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
|
||||
assertThat(historyEntry.getRequestedByRegistrar()).isFalse();
|
||||
assertThat(historyEntry.getBySuperuser()).isTrue();
|
||||
DatastoreHelper.assertNoBillingEvents();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createUnlock_alreadyPendingUnlock() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
|
||||
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A pending unlock action already exists for example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createUnlock_nonAdminUnlockingAdmin() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Non-admin user cannot unlock admin-locked domain example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createLock_unknownDomain() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
"asdf.tld", "TheRegistrar", POC_ID, false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Unknown domain asdf.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createLock_alreadyPendingLock() {
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A pending or completed lock action already exists for example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createLock_alreadyLocked() {
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_createUnlock_alreadyUnlocked() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.createRegistryUnlockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already unlocked");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_applyLock_alreadyApplied() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
domain = reloadDomain();
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_applyLock_expired() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("The pending lock has expired; please try again");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_applyLock_nonAdmin_applyAdminLock() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Non-admin user cannot complete admin lock");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_applyUnlock_alreadyUnlocked() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
|
||||
RegistryLock unlock =
|
||||
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
|
||||
DomainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
|
||||
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
DomainLockUtils.verifyAndApplyUnlock(
|
||||
unlock.getVerificationCode(), false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already unlocked");
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_applyLock_alreadyLocked() {
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
|
||||
String verificationCode = lock.getVerificationCode();
|
||||
// reload to pick up modification times, etc
|
||||
lock = RegistryLockDao.getByVerificationCode(verificationCode).get();
|
||||
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> DomainLockUtils.verifyAndApplyLock(verificationCode, false, clock)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Domain example.tld is already locked");
|
||||
|
||||
// Failure during Datastore portion shouldn't affect the SQL object
|
||||
RegistryLock afterAction =
|
||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||
assertThat(afterAction).isEqualTo(lock);
|
||||
assertNoDomainChanges();
|
||||
}
|
||||
|
||||
private DomainBase reloadDomain() {
|
||||
return ofy().load().entity(domain).now();
|
||||
}
|
||||
|
||||
private void assertNoDomainChanges() {
|
||||
assertThat(reloadDomain()).isEqualTo(domain);
|
||||
}
|
||||
|
||||
private void assertBillingEvent(HistoryEntry historyEntry) {
|
||||
assertBillingEvents(ImmutableList.of(historyEntry));
|
||||
}
|
||||
|
||||
private void assertBillingEvents(ImmutableList<HistoryEntry> historyEntries) {
|
||||
Set<BillingEvent> expectedEvents =
|
||||
historyEntries.stream()
|
||||
.map(
|
||||
entry ->
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.SERVER_STATUS)
|
||||
.setTargetId(domain.getForeignKey())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setCost(Registry.get(domain.getTld()).getServerStatusChangeCost())
|
||||
.setEventTime(clock.nowUtc())
|
||||
.setBillingTime(clock.nowUtc())
|
||||
.setParent(entry)
|
||||
.build())
|
||||
.collect(Collectors.toSet());
|
||||
DatastoreHelper.assertBillingEvents(expectedEvents);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ package google.registry.tools;
|
|||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.newDomainBase;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatastoreHelper.persistNewRegistrar;
|
||||
|
@ -24,55 +25,75 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT
|
|||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationTestRule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Unit tests for {@link LockDomainCommand}. */
|
||||
public class LockDomainCommandTest extends EppToolCommandTestCase<LockDomainCommand> {
|
||||
public class LockDomainCommandTest extends CommandTestCase<LockDomainCommand> {
|
||||
|
||||
@Rule
|
||||
public final JpaIntegrationTestRule jpaRule =
|
||||
new JpaTestRules.Builder().buildIntegrationTestRule();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
eppVerifier.expectSuperuser();
|
||||
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
||||
createTld("tld");
|
||||
command.registryAdminClientId = "adminreg";
|
||||
command.clock = new FakeClock();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_sendsCorrectEppXml() throws Exception {
|
||||
persistActiveDomain("example.tld");
|
||||
public void testSuccess_locksDomain() throws Exception {
|
||||
DomainBase domain = persistActiveDomain("example.tld");
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
eppVerifier.verifySent("domain_lock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertThat(reloadResource(domain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
||||
DomainBase domain =
|
||||
persistResource(
|
||||
newDomainBase("example.tld")
|
||||
.asBuilder()
|
||||
.addStatusValue(SERVER_TRANSFER_PROHIBITED)
|
||||
.build());
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
eppVerifier.verifySent("domain_lock_partial_statuses.xml");
|
||||
assertThat(reloadResource(domain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_manyDomains() throws Exception {
|
||||
// Create 26 domains -- one more than the number of entity groups allowed in a transaction (in
|
||||
// case that was going to be the failure point).
|
||||
List<String> domains = new ArrayList<>();
|
||||
List<DomainBase> domains = new ArrayList<>();
|
||||
for (int n = 0; n < 26; n++) {
|
||||
String domain = String.format("domain%d.tld", n);
|
||||
persistActiveDomain(domain);
|
||||
domains.add(domain);
|
||||
domains.add(persistActiveDomain(domain));
|
||||
}
|
||||
runCommandForced(
|
||||
ImmutableList.<String>builder().add("--client=NewRegistrar").addAll(domains).build());
|
||||
for (String domain : domains) {
|
||||
eppVerifier.verifySent("domain_lock.xml", ImmutableMap.of("DOMAIN", domain));
|
||||
ImmutableList.<String>builder()
|
||||
.add("--client=NewRegistrar")
|
||||
.addAll(
|
||||
domains.stream()
|
||||
.map(DomainBase::getFullyQualifiedDomainName)
|
||||
.collect(Collectors.toList()))
|
||||
.build());
|
||||
for (DomainBase domain : domains) {
|
||||
assertThat(reloadResource(domain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,21 +108,22 @@ public class LockDomainCommandTest extends EppToolCommandTestCase<LockDomainComm
|
|||
|
||||
@Test
|
||||
public void testSuccess_alreadyLockedDomain_performsNoAction() throws Exception {
|
||||
DomainBase domain =
|
||||
persistResource(
|
||||
newDomainBase("example.tld")
|
||||
.asBuilder()
|
||||
.addStatusValues(REGISTRY_LOCK_STATUSES)
|
||||
.build());
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
assertThat(reloadResource(domain)).isEqualTo(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
||||
persistActiveDomain("example.tld");
|
||||
DomainBase domain = persistActiveDomain("example.tld");
|
||||
runCommandForced("example.tld");
|
||||
eppVerifier
|
||||
.expectClientId("adminreg")
|
||||
.verifySent("domain_lock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertThat(RegistryLockDao.getMostRecentByRepoId(domain.getRepoId()).get().getRegistrarId())
|
||||
.isEqualTo("adminreg");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -16,75 +16,95 @@ package google.registry.tools;
|
|||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.newDomainBase;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatastoreHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationTestRule;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Unit tests for {@link UnlockDomainCommand}. */
|
||||
public class UnlockDomainCommandTest extends EppToolCommandTestCase<UnlockDomainCommand> {
|
||||
public class UnlockDomainCommandTest extends CommandTestCase<UnlockDomainCommand> {
|
||||
|
||||
@Rule
|
||||
public final JpaIntegrationTestRule jpaRule =
|
||||
new JpaTestRules.Builder().buildIntegrationTestRule();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
eppVerifier.expectSuperuser();
|
||||
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
||||
createTld("tld");
|
||||
command.registryAdminClientId = "adminreg";
|
||||
command.clock = new FakeClock();
|
||||
}
|
||||
|
||||
private static void persistLockedDomain(String domainName) {
|
||||
persistResource(
|
||||
newDomainBase(domainName)
|
||||
.asBuilder()
|
||||
.addStatusValues(
|
||||
ImmutableSet.of(
|
||||
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
||||
.build());
|
||||
private DomainBase persistLockedDomain(String domainName, String registrarId) {
|
||||
DomainBase domain = persistResource(newDomainBase(domainName));
|
||||
RegistryLock lock =
|
||||
DomainLockUtils.createRegistryLockRequest(
|
||||
domainName, registrarId, null, true, command.clock);
|
||||
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, command.clock);
|
||||
return reloadResource(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_sendsCorrectEppXml() throws Exception {
|
||||
persistLockedDomain("example.tld");
|
||||
public void testSuccess_unlocksDomain() throws Exception {
|
||||
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
eppVerifier.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertThat(reloadResource(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
||||
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||
domain =
|
||||
persistResource(
|
||||
newDomainBase("example.tld")
|
||||
domain
|
||||
.asBuilder()
|
||||
.addStatusValues(ImmutableSet.of(SERVER_DELETE_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
||||
.setStatusValues(
|
||||
ImmutableSet.of(SERVER_DELETE_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
||||
.build());
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
eppVerifier.verifySent("domain_unlock_partial_statuses.xml");
|
||||
assertThat(reloadResource(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_manyDomains() throws Exception {
|
||||
// Create 26 domains -- one more than the number of entity groups allowed in a transaction (in
|
||||
// case that was going to be the failure point).
|
||||
List<String> domains = new ArrayList<>();
|
||||
List<DomainBase> domains = new ArrayList<>();
|
||||
for (int n = 0; n < 26; n++) {
|
||||
String domain = String.format("domain%d.tld", n);
|
||||
persistLockedDomain(domain);
|
||||
domains.add(domain);
|
||||
domains.add(persistLockedDomain(domain, "NewRegistrar"));
|
||||
}
|
||||
runCommandForced(
|
||||
ImmutableList.<String>builder().add("--client=NewRegistrar").addAll(domains).build());
|
||||
for (String domain : domains) {
|
||||
eppVerifier.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", domain));
|
||||
ImmutableList.<String>builder()
|
||||
.add("--client=NewRegistrar")
|
||||
.addAll(
|
||||
domains.stream()
|
||||
.map(DomainBase::getFullyQualifiedDomainName)
|
||||
.collect(Collectors.toList()))
|
||||
.build());
|
||||
for (DomainBase domain : domains) {
|
||||
assertThat(reloadResource(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,17 +119,17 @@ public class UnlockDomainCommandTest extends EppToolCommandTestCase<UnlockDomain
|
|||
|
||||
@Test
|
||||
public void testSuccess_alreadyUnlockedDomain_performsNoAction() throws Exception {
|
||||
persistActiveDomain("example.tld");
|
||||
DomainBase domain = persistActiveDomain("example.tld");
|
||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||
assertThat(reloadResource(domain)).isEqualTo(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
||||
persistLockedDomain("example.tld");
|
||||
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||
runCommandForced("example.tld");
|
||||
eppVerifier
|
||||
.expectClientId("adminreg")
|
||||
.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
assertThat(RegistryLockDao.getMostRecentByRepoId(domain.getRepoId()).get().getRegistrarId())
|
||||
.isEqualTo("adminreg");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverDeleteProhibited"/>
|
||||
<domain:status s="serverTransferProhibited"/>
|
||||
<domain:status s="serverUpdateProhibited"/>
|
||||
</domain:add>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverDeleteProhibited"/>
|
||||
<domain:status s="serverUpdateProhibited"/>
|
||||
</domain:add>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
<domain:rem>
|
||||
<domain:status s="serverDeleteProhibited"/>
|
||||
<domain:status s="serverTransferProhibited"/>
|
||||
<domain:status s="serverUpdateProhibited"/>
|
||||
</domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:rem>
|
||||
<domain:status s="serverDeleteProhibited"/>
|
||||
<domain:status s="serverUpdateProhibited"/>
|
||||
</domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
Loading…
Add table
Add a link
Reference in a new issue