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
|
* code (there may be two instances of the same code in the database--one after lock object
|
||||||
* creation and one after verification.
|
* creation and one after verification.
|
||||||
*/
|
*/
|
||||||
public static RegistryLock getByVerificationCode(String verificationCode) {
|
public static Optional<RegistryLock> getByVerificationCode(String verificationCode) {
|
||||||
return jpaTm()
|
return jpaTm()
|
||||||
.transact(
|
.transact(
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -42,9 +42,8 @@ public final class RegistryLockDao {
|
||||||
Long.class)
|
Long.class)
|
||||||
.setParameter("verificationCode", verificationCode)
|
.setParameter("verificationCode", verificationCode)
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
// TODO(gbrodman): Don't throw NPE here. Maybe NoResultException fits better?
|
return Optional.ofNullable(revisionId)
|
||||||
checkNotNull(revisionId, "No registry lock with this code");
|
.map(revision -> em.find(RegistryLock.class, revision));
|
||||||
return em.find(RegistryLock.class, revisionId);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
* Returns the most recent lock object for a given domain specified by repo ID, or empty if this
|
||||||
* hasn't been locked before.
|
* domain hasn't been locked before.
|
||||||
*/
|
*/
|
||||||
public static Optional<RegistryLock> getMostRecentByRepoId(String repoId) {
|
public static Optional<RegistryLock> getMostRecentByRepoId(String repoId) {
|
||||||
return jpaTm()
|
return jpaTm()
|
||||||
|
@ -86,6 +85,28 @@ public final class RegistryLockDao {
|
||||||
.findFirst());
|
.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) {
|
public static RegistryLock save(RegistryLock registryLock) {
|
||||||
checkNotNull(registryLock, "Null registry lock cannot be saved");
|
checkNotNull(registryLock, "Null registry lock cannot be saved");
|
||||||
return jpaTm().transact(() -> jpaTm().getEntityManager().merge(registryLock));
|
return jpaTm().transact(() -> jpaTm().getEntityManager().merge(registryLock));
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package google.registry.schema.domain;
|
package google.registry.schema.domain;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
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.DateTimeUtils.toZonedDateTime;
|
||||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
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.CreateAutoTimestamp;
|
||||||
import google.registry.model.ImmutableObject;
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.UpdateAutoTimestamp;
|
import google.registry.model.UpdateAutoTimestamp;
|
||||||
|
import google.registry.util.Clock;
|
||||||
import google.registry.util.DateTimeUtils;
|
import google.registry.util.DateTimeUtils;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -182,6 +184,20 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||||
return lockCompletionTimestamp != null && unlockCompletionTimestamp == null;
|
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
|
@Override
|
||||||
public Builder asBuilder() {
|
public Builder asBuilder() {
|
||||||
return new Builder(clone(this));
|
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;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
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.beust.jcommander.Parameters;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.flogger.FluentLogger;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.template.soy.data.SoyMapData;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.tools.soy.DomainUpdateSoyInfo;
|
import google.registry.schema.domain.RegistryLock;
|
||||||
import java.util.Optional;
|
|
||||||
import org.joda.time.DateTime;
|
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.
|
* <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();
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initMutatingEppToolCommand() {
|
protected ImmutableSet<String> getRelevantDomains() {
|
||||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
// 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()) {
|
for (String domain : getDomains()) {
|
||||||
Optional<DomainBase> domainBase = loadByForeignKey(DomainBase.class, domain, now);
|
DomainBase domainBase =
|
||||||
checkArgumentPresent(domainBase, "Domain '%s' does not exist or is deleted", domain);
|
loadByForeignKey(DomainBase.class, domain, now)
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||||
ImmutableSet<StatusValue> statusesToAdd =
|
ImmutableSet<StatusValue> statusesToAdd =
|
||||||
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.get().getStatusValues())
|
Sets.difference(REGISTRY_LOCK_STATUSES, domainBase.getStatusValues()).immutableCopy();
|
||||||
.immutableCopy();
|
|
||||||
if (statusesToAdd.isEmpty()) {
|
if (statusesToAdd.isEmpty()) {
|
||||||
logger.atInfo().log("Domain '%s' is already locked and needs no updates.", domain);
|
logger.atInfo().log("Domain '%s' is already locked and needs no updates.", domain);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
relevantDomains.add(domain);
|
||||||
|
}
|
||||||
|
return relevantDomains.build();
|
||||||
|
}
|
||||||
|
|
||||||
setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE);
|
@Override
|
||||||
addSoyRecord(
|
protected RegistryLock createLock(String domain) {
|
||||||
clientId,
|
return DomainLockUtils.createRegistryLockRequest(domain, clientId, null, true, clock);
|
||||||
new SoyMapData(
|
}
|
||||||
"domain", domain,
|
|
||||||
"add", true,
|
@Override
|
||||||
"addNameservers", ImmutableList.of(),
|
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||||
"addAdmins", ImmutableList.of(),
|
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
|
||||||
"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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,23 @@ import static google.registry.util.CollectionUtils.findDuplicates;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.flogger.FluentLogger;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.schema.domain.RegistryLock;
|
||||||
|
import google.registry.util.Clock;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/** Shared base class for commands to registry lock or unlock a domain via EPP. */
|
/** 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(
|
ImmutableSet.of(
|
||||||
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
|
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
|
||||||
|
|
||||||
|
@ -48,23 +55,52 @@ public abstract class LockOrUnlockDomainCommand extends MutatingEppToolCommand {
|
||||||
@Config("registryAdminClientId")
|
@Config("registryAdminClientId")
|
||||||
String registryAdminClientId;
|
String registryAdminClientId;
|
||||||
|
|
||||||
|
@Inject Clock clock;
|
||||||
|
|
||||||
|
protected ImmutableSet<String> relevantDomains = ImmutableSet.of();
|
||||||
|
|
||||||
protected ImmutableSet<String> getDomains() {
|
protected ImmutableSet<String> getDomains() {
|
||||||
return ImmutableSet.copyOf(mainParameters);
|
return ImmutableSet.copyOf(mainParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initEppToolCommand() throws Exception {
|
protected void init() {
|
||||||
// Superuser status is required to update registry lock statuses.
|
|
||||||
superuser = true;
|
|
||||||
|
|
||||||
// Default clientId to the registry registrar account if otherwise unspecified.
|
// Default clientId to the registry registrar account if otherwise unspecified.
|
||||||
if (clientId == null) {
|
if (clientId == null) {
|
||||||
clientId = registryAdminClientId;
|
clientId = registryAdminClientId;
|
||||||
}
|
}
|
||||||
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
|
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
|
||||||
checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates);
|
checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates);
|
||||||
initMutatingEppToolCommand();
|
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"== ENSURE THAT YOU HAVE AUTHENTICATED THE REGISTRAR BEFORE RUNNING THIS COMMAND ==");
|
"== 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;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
|
||||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
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.beust.jcommander.Parameters;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.flogger.FluentLogger;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.template.soy.data.SoyMapData;
|
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.tools.soy.DomainUpdateSoyInfo;
|
import google.registry.schema.domain.RegistryLock;
|
||||||
import java.util.Optional;
|
|
||||||
import org.joda.time.DateTime;
|
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.
|
* <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();
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initMutatingEppToolCommand() {
|
protected ImmutableSet<String> getRelevantDomains() {
|
||||||
// Project all domains as of the same time so that argument order doesn't affect behavior.
|
// 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()) {
|
for (String domain : getDomains()) {
|
||||||
Optional<DomainBase> domainBase = loadByForeignKey(DomainBase.class, domain, now);
|
DomainBase domainBase =
|
||||||
checkArgumentPresent(domainBase, "Domain '%s' does not exist or is deleted", domain);
|
loadByForeignKey(DomainBase.class, domain, now)
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new IllegalArgumentException(
|
||||||
|
String.format("Domain '%s' does not exist or is deleted", domain)));
|
||||||
ImmutableSet<StatusValue> statusesToRemove =
|
ImmutableSet<StatusValue> statusesToRemove =
|
||||||
Sets.intersection(domainBase.get().getStatusValues(), REGISTRY_LOCK_STATUSES)
|
Sets.intersection(domainBase.getStatusValues(), REGISTRY_LOCK_STATUSES).immutableCopy();
|
||||||
.immutableCopy();
|
|
||||||
if (statusesToRemove.isEmpty()) {
|
if (statusesToRemove.isEmpty()) {
|
||||||
logger.atInfo().log("Domain '%s' is already unlocked and needs no updates.", domain);
|
logger.atInfo().log("Domain '%s' is already unlocked and needs no updates.", domain);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
relevantDomains.add(domain);
|
||||||
|
}
|
||||||
|
return relevantDomains.build();
|
||||||
|
}
|
||||||
|
|
||||||
setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE);
|
@Override
|
||||||
addSoyRecord(
|
protected RegistryLock createLock(String domain) {
|
||||||
clientId,
|
return DomainLockUtils.createRegistryUnlockRequest(domain, clientId, true, clock);
|
||||||
new SoyMapData(
|
}
|
||||||
"domain", domain,
|
|
||||||
"add", false,
|
@Override
|
||||||
"addNameservers", ImmutableList.of(),
|
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
|
||||||
"addAdmins", ImmutableList.of(),
|
DomainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), true, clock);
|
||||||
"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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,23 +44,13 @@ public final class RegistryLockDaoTest {
|
||||||
public void testSaveAndLoad_success() {
|
public void testSaveAndLoad_success() {
|
||||||
RegistryLock lock = createLock();
|
RegistryLock lock = createLock();
|
||||||
RegistryLockDao.save(lock);
|
RegistryLockDao.save(lock);
|
||||||
RegistryLock fromDatabase = RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
RegistryLock fromDatabase =
|
||||||
|
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||||
assertThat(fromDatabase.getDomainName()).isEqualTo(lock.getDomainName());
|
assertThat(fromDatabase.getDomainName()).isEqualTo(lock.getDomainName());
|
||||||
assertThat(fromDatabase.getVerificationCode()).isEqualTo(lock.getVerificationCode());
|
assertThat(fromDatabase.getVerificationCode()).isEqualTo(lock.getVerificationCode());
|
||||||
assertThat(fromDatabase.getLastUpdateTimestamp()).isEqualTo(jpaRule.getTxnClock().nowUtc());
|
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
|
@Test
|
||||||
public void testSaveTwiceAndLoad_returnsLatest() {
|
public void testSaveTwiceAndLoad_returnsLatest() {
|
||||||
RegistryLock lock = createLock();
|
RegistryLock lock = createLock();
|
||||||
|
@ -70,7 +60,7 @@ public final class RegistryLockDaoTest {
|
||||||
.transact(
|
.transact(
|
||||||
() -> {
|
() -> {
|
||||||
RegistryLock updatedLock =
|
RegistryLock updatedLock =
|
||||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||||
RegistryLockDao.save(
|
RegistryLockDao.save(
|
||||||
updatedLock
|
updatedLock
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
|
@ -81,7 +71,7 @@ public final class RegistryLockDaoTest {
|
||||||
.transact(
|
.transact(
|
||||||
() -> {
|
() -> {
|
||||||
RegistryLock fromDatabase =
|
RegistryLock fromDatabase =
|
||||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||||
assertThat(fromDatabase.getLockCompletionTimestamp().get())
|
assertThat(fromDatabase.getLockCompletionTimestamp().get())
|
||||||
.isEqualTo(jpaRule.getTxnClock().nowUtc());
|
.isEqualTo(jpaRule.getTxnClock().nowUtc());
|
||||||
assertThat(fromDatabase.getLastUpdateTimestamp())
|
assertThat(fromDatabase.getLastUpdateTimestamp())
|
||||||
|
@ -100,7 +90,8 @@ public final class RegistryLockDaoTest {
|
||||||
.setUnlockCompletionTimestamp(jpaRule.getTxnClock().nowUtc())
|
.setUnlockCompletionTimestamp(jpaRule.getTxnClock().nowUtc())
|
||||||
.build());
|
.build());
|
||||||
RegistryLockDao.save(lock);
|
RegistryLockDao.save(lock);
|
||||||
RegistryLock fromDatabase = RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
RegistryLock fromDatabase =
|
||||||
|
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||||
assertThat(fromDatabase.getUnlockRequestTimestamp())
|
assertThat(fromDatabase.getUnlockRequestTimestamp())
|
||||||
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
||||||
assertThat(fromDatabase.getUnlockCompletionTimestamp())
|
assertThat(fromDatabase.getUnlockCompletionTimestamp())
|
||||||
|
@ -119,7 +110,7 @@ public final class RegistryLockDaoTest {
|
||||||
.transact(
|
.transact(
|
||||||
() -> {
|
() -> {
|
||||||
RegistryLock fromDatabase =
|
RegistryLock fromDatabase =
|
||||||
RegistryLockDao.getByVerificationCode(lock.getVerificationCode());
|
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
|
||||||
assertThat(fromDatabase.getLockCompletionTimestamp())
|
assertThat(fromDatabase.getLockCompletionTimestamp())
|
||||||
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
.isEqualTo(Optional.of(jpaRule.getTxnClock().nowUtc()));
|
||||||
});
|
});
|
||||||
|
@ -130,6 +121,11 @@ public final class RegistryLockDaoTest {
|
||||||
assertThrows(NullPointerException.class, () -> RegistryLockDao.save(null));
|
assertThrows(NullPointerException.class, () -> RegistryLockDao.save(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getLock_unknownCode() {
|
||||||
|
assertThat(RegistryLockDao.getByVerificationCode("hi").isPresent()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoad_lockedDomains_byRegistrarId() {
|
public void testLoad_lockedDomains_byRegistrarId() {
|
||||||
RegistryLock lock =
|
RegistryLock lock =
|
||||||
|
@ -180,6 +176,28 @@ public final class RegistryLockDaoTest {
|
||||||
assertThat(RegistryLockDao.getMostRecentByRepoId("nonexistent").isPresent()).isFalse();
|
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() {
|
private RegistryLock createLock() {
|
||||||
return new RegistryLock.Builder()
|
return new RegistryLock.Builder()
|
||||||
.setRepoId("repoId")
|
.setRepoId("repoId")
|
||||||
|
|
|
@ -22,6 +22,9 @@ import google.registry.schema.tld.PremiumListUtilsTest;
|
||||||
import google.registry.schema.tld.ReservedListDaoTest;
|
import google.registry.schema.tld.ReservedListDaoTest;
|
||||||
import google.registry.schema.tmch.ClaimsListDaoTest;
|
import google.registry.schema.tmch.ClaimsListDaoTest;
|
||||||
import google.registry.tools.CreateReservedListCommandTest;
|
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.UpdateReservedListCommandTest;
|
||||||
import google.registry.tools.server.CreatePremiumListActionTest;
|
import google.registry.tools.server.CreatePremiumListActionTest;
|
||||||
import google.registry.tools.server.UpdatePremiumListActionTest;
|
import google.registry.tools.server.UpdatePremiumListActionTest;
|
||||||
|
@ -42,14 +45,17 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||||
@RunWith(Suite.class)
|
@RunWith(Suite.class)
|
||||||
@SuiteClasses({
|
@SuiteClasses({
|
||||||
ClaimsListDaoTest.class,
|
ClaimsListDaoTest.class,
|
||||||
|
CreatePremiumListActionTest.class,
|
||||||
CreateReservedListCommandTest.class,
|
CreateReservedListCommandTest.class,
|
||||||
CursorDaoTest.class,
|
CursorDaoTest.class,
|
||||||
CreatePremiumListActionTest.class,
|
DomainLockUtilsTest.class,
|
||||||
|
LockDomainCommandTest.class,
|
||||||
PremiumListDaoTest.class,
|
PremiumListDaoTest.class,
|
||||||
PremiumListUtilsTest.class,
|
PremiumListUtilsTest.class,
|
||||||
RegistryLockDaoTest.class,
|
RegistryLockDaoTest.class,
|
||||||
RegistryLockGetActionTest.class,
|
RegistryLockGetActionTest.class,
|
||||||
ReservedListDaoTest.class,
|
ReservedListDaoTest.class,
|
||||||
|
UnlockDomainCommandTest.class,
|
||||||
UpdatePremiumListActionTest.class,
|
UpdatePremiumListActionTest.class,
|
||||||
UpdateReservedListCommandTest.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
|
* Returns all of the history entries that are parented off the given EppResource with the given
|
||||||
* type.
|
* type.
|
||||||
*/
|
*/
|
||||||
public static List<HistoryEntry> getHistoryEntriesOfType(
|
public static ImmutableList<HistoryEntry> getHistoryEntriesOfType(
|
||||||
EppResource resource, final HistoryEntry.Type type) {
|
EppResource resource, final HistoryEntry.Type type) {
|
||||||
return getHistoryEntries(resource)
|
return getHistoryEntries(resource)
|
||||||
.stream()
|
.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 com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
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.newDomainBase;
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||||
import static google.registry.testing.DatastoreHelper.persistNewRegistrar;
|
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 static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
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.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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/** Unit tests for {@link LockDomainCommand}. */
|
/** 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
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
eppVerifier.expectSuperuser();
|
|
||||||
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
||||||
|
createTld("tld");
|
||||||
command.registryAdminClientId = "adminreg";
|
command.registryAdminClientId = "adminreg";
|
||||||
|
command.clock = new FakeClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_sendsCorrectEppXml() throws Exception {
|
public void testSuccess_locksDomain() throws Exception {
|
||||||
persistActiveDomain("example.tld");
|
DomainBase domain = persistActiveDomain("example.tld");
|
||||||
runCommandForced("--client=NewRegistrar", "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
|
@Test
|
||||||
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
||||||
|
DomainBase domain =
|
||||||
persistResource(
|
persistResource(
|
||||||
newDomainBase("example.tld")
|
newDomainBase("example.tld")
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addStatusValue(SERVER_TRANSFER_PROHIBITED)
|
.addStatusValue(SERVER_TRANSFER_PROHIBITED)
|
||||||
.build());
|
.build());
|
||||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||||
eppVerifier.verifySent("domain_lock_partial_statuses.xml");
|
assertThat(reloadResource(domain).getStatusValues())
|
||||||
|
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_manyDomains() throws Exception {
|
public void testSuccess_manyDomains() throws Exception {
|
||||||
// Create 26 domains -- one more than the number of entity groups allowed in a transaction (in
|
// 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).
|
// 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++) {
|
for (int n = 0; n < 26; n++) {
|
||||||
String domain = String.format("domain%d.tld", n);
|
String domain = String.format("domain%d.tld", n);
|
||||||
persistActiveDomain(domain);
|
domains.add(persistActiveDomain(domain));
|
||||||
domains.add(domain);
|
|
||||||
}
|
}
|
||||||
runCommandForced(
|
runCommandForced(
|
||||||
ImmutableList.<String>builder().add("--client=NewRegistrar").addAll(domains).build());
|
ImmutableList.<String>builder()
|
||||||
for (String domain : domains) {
|
.add("--client=NewRegistrar")
|
||||||
eppVerifier.verifySent("domain_lock.xml", ImmutableMap.of("DOMAIN", domain));
|
.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
|
@Test
|
||||||
public void testSuccess_alreadyLockedDomain_performsNoAction() throws Exception {
|
public void testSuccess_alreadyLockedDomain_performsNoAction() throws Exception {
|
||||||
|
DomainBase domain =
|
||||||
persistResource(
|
persistResource(
|
||||||
newDomainBase("example.tld")
|
newDomainBase("example.tld")
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addStatusValues(REGISTRY_LOCK_STATUSES)
|
.addStatusValues(REGISTRY_LOCK_STATUSES)
|
||||||
.build());
|
.build());
|
||||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||||
|
assertThat(reloadResource(domain)).isEqualTo(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
||||||
persistActiveDomain("example.tld");
|
DomainBase domain = persistActiveDomain("example.tld");
|
||||||
runCommandForced("example.tld");
|
runCommandForced("example.tld");
|
||||||
eppVerifier
|
assertThat(RegistryLockDao.getMostRecentByRepoId(domain.getRepoId()).get().getRegistrarId())
|
||||||
.expectClientId("adminreg")
|
.isEqualTo("adminreg");
|
||||||
.verifySent("domain_lock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -16,75 +16,95 @@ package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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_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.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.newDomainBase;
|
||||||
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
|
||||||
import static google.registry.testing.DatastoreHelper.persistNewRegistrar;
|
import static google.registry.testing.DatastoreHelper.persistNewRegistrar;
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||||
|
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.registrar.Registrar.Type;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/** Unit tests for {@link UnlockDomainCommand}. */
|
/** 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
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
eppVerifier.expectSuperuser();
|
|
||||||
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
persistNewRegistrar("adminreg", "Admin Registrar", Type.REAL, 693L);
|
||||||
|
createTld("tld");
|
||||||
command.registryAdminClientId = "adminreg";
|
command.registryAdminClientId = "adminreg";
|
||||||
|
command.clock = new FakeClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void persistLockedDomain(String domainName) {
|
private DomainBase persistLockedDomain(String domainName, String registrarId) {
|
||||||
persistResource(
|
DomainBase domain = persistResource(newDomainBase(domainName));
|
||||||
newDomainBase(domainName)
|
RegistryLock lock =
|
||||||
.asBuilder()
|
DomainLockUtils.createRegistryLockRequest(
|
||||||
.addStatusValues(
|
domainName, registrarId, null, true, command.clock);
|
||||||
ImmutableSet.of(
|
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, command.clock);
|
||||||
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
return reloadResource(domain);
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_sendsCorrectEppXml() throws Exception {
|
public void testSuccess_unlocksDomain() throws Exception {
|
||||||
persistLockedDomain("example.tld");
|
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||||
eppVerifier.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
assertThat(reloadResource(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
public void testSuccess_partiallyUpdatesStatuses() throws Exception {
|
||||||
|
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||||
|
domain =
|
||||||
persistResource(
|
persistResource(
|
||||||
newDomainBase("example.tld")
|
domain
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addStatusValues(ImmutableSet.of(SERVER_DELETE_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
.setStatusValues(
|
||||||
|
ImmutableSet.of(SERVER_DELETE_PROHIBITED, SERVER_UPDATE_PROHIBITED))
|
||||||
.build());
|
.build());
|
||||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||||
eppVerifier.verifySent("domain_unlock_partial_statuses.xml");
|
assertThat(reloadResource(domain).getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_manyDomains() throws Exception {
|
public void testSuccess_manyDomains() throws Exception {
|
||||||
// Create 26 domains -- one more than the number of entity groups allowed in a transaction (in
|
// 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).
|
// 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++) {
|
for (int n = 0; n < 26; n++) {
|
||||||
String domain = String.format("domain%d.tld", n);
|
String domain = String.format("domain%d.tld", n);
|
||||||
persistLockedDomain(domain);
|
domains.add(persistLockedDomain(domain, "NewRegistrar"));
|
||||||
domains.add(domain);
|
|
||||||
}
|
}
|
||||||
runCommandForced(
|
runCommandForced(
|
||||||
ImmutableList.<String>builder().add("--client=NewRegistrar").addAll(domains).build());
|
ImmutableList.<String>builder()
|
||||||
for (String domain : domains) {
|
.add("--client=NewRegistrar")
|
||||||
eppVerifier.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", domain));
|
.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
|
@Test
|
||||||
public void testSuccess_alreadyUnlockedDomain_performsNoAction() throws Exception {
|
public void testSuccess_alreadyUnlockedDomain_performsNoAction() throws Exception {
|
||||||
persistActiveDomain("example.tld");
|
DomainBase domain = persistActiveDomain("example.tld");
|
||||||
runCommandForced("--client=NewRegistrar", "example.tld");
|
runCommandForced("--client=NewRegistrar", "example.tld");
|
||||||
|
assertThat(reloadResource(domain)).isEqualTo(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
public void testSuccess_defaultsToAdminRegistrar_ifUnspecified() throws Exception {
|
||||||
persistLockedDomain("example.tld");
|
DomainBase domain = persistLockedDomain("example.tld", "NewRegistrar");
|
||||||
runCommandForced("example.tld");
|
runCommandForced("example.tld");
|
||||||
eppVerifier
|
assertThat(RegistryLockDao.getMostRecentByRepoId(domain.getRepoId()).get().getRegistrarId())
|
||||||
.expectClientId("adminreg")
|
.isEqualTo("adminreg");
|
||||||
.verifySent("domain_unlock.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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