Convert AuthenticatedRegAccessor and OteStats to SQL (#1039)

This required adding a new HistoryEntryDao method but it's fairly
similar to the ones we already have.
This commit is contained in:
gbrodman 2021-04-02 11:41:26 -04:00 committed by GitHub
parent 4e7dd7a95a
commit c077aca433
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 230 additions and 119 deletions

View file

@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY; import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@ -41,10 +40,10 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.PremiumListDualDao; import google.registry.model.registry.label.PremiumListDualDao;
import google.registry.persistence.VKey;
import google.registry.util.CidrAddressBlock; import google.registry.util.CidrAddressBlock;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
@ -225,15 +224,14 @@ public final class OteAccountBuilder {
} }
/** /**
* Persists all the OT&E entities to datastore. * Persists all the OT&E entities to the database.
* *
* @return map from the new clientIds created to the new TLDs they have access to. Can be used to * @return map from the new clientIds created to the new TLDs they have access to. Can be used to
* go over all the newly created Registrars / Registries / RegistrarContacts if any * go over all the newly created Registrars / Registries / RegistrarContacts if any
* post-creation work is needed. * post-creation work is needed.
*/ */
public ImmutableMap<String, String> buildAndPersist() { public ImmutableMap<String, String> buildAndPersist() {
// save all the entitiesl in a single transaction saveAllEntities();
tm().transact(this::saveAllEntities);
return clientIdToTld; return clientIdToTld;
} }
@ -246,30 +244,38 @@ public final class OteAccountBuilder {
/** Saves all the OT&amp;E entities we created. */ /** Saves all the OT&amp;E entities we created. */
private void saveAllEntities() { private void saveAllEntities() {
tm().assertInTransaction();
// use ImmutableObject instead of Registry so that the Key generation doesn't break // use ImmutableObject instead of Registry so that the Key generation doesn't break
ImmutableList<ImmutableObject> registries = ImmutableList.of(sunriseTld, gaTld, eapTld); ImmutableList<Registry> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
ImmutableList<RegistrarContact> contacts = contactsBuilder.build(); ImmutableList<RegistrarContact> contacts = contactsBuilder.build();
if (!replaceExisting) { tm().transact(
ImmutableList<Key<ImmutableObject>> keys = () -> {
Streams.concat(registries.stream(), registrars.stream(), contacts.stream()) if (!replaceExisting) {
.map(Key::create) ImmutableList<VKey<? extends ImmutableObject>> keys =
.collect(toImmutableList()); Streams.concat(
Set<Key<ImmutableObject>> existingKeys = ofy().load().keys(keys).keySet(); registries.stream()
checkState( .map(registry -> Registry.createVKey(registry.getTldStr())),
existingKeys.isEmpty(), registrars.stream().map(Registrar::createVKey),
"Found existing object(s) conflicting with OT&E objects: %s", contacts.stream().map(RegistrarContact::createVKey))
existingKeys); .collect(toImmutableList());
} ImmutableMap<VKey<? extends ImmutableObject>, ImmutableObject> existingObjects =
// Save the Registries (TLDs) first tm().loadByKeysIfPresent(keys);
ofy().save().entities(registries).now(); checkState(
// Now we can set the allowedTlds for the registrars existingObjects.isEmpty(),
registrars = registrars.stream().map(this::addAllowedTld).collect(toImmutableList()); "Found existing object(s) conflicting with OT&E objects: %s",
// and we can save the registrars and contacts! existingObjects.keySet());
ofy().save().entities(registrars); }
ofy().save().entities(contacts); // Save the Registries (TLDs) first
tm().putAll(registries);
});
// Now we can set the allowedTlds for the registrars in a new transaction
tm().transact(
() -> {
registrars = registrars.stream().map(this::addAllowedTld).collect(toImmutableList());
// and we can save the registrars and contacts!
tm().putAll(registrars);
tm().putAll(contacts);
});
} }
private Registrar addAllowedTld(Registrar registrar) { private Registrar addAllowedTld(Registrar registrar) {

View file

@ -17,7 +17,6 @@ package google.registry.model;
import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.equalTo;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.eppcommon.EppXmlTransformer.unmarshal; import static google.registry.model.eppcommon.EppXmlTransformer.unmarshal;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.isNullOrEmpty;
import static google.registry.util.DomainNameUtils.ACE_PREFIX; import static google.registry.util.DomainNameUtils.ACE_PREFIX;
@ -28,7 +27,6 @@ import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multiset; import com.google.common.collect.Multiset;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeCreateCommandExtension;
import google.registry.model.domain.launch.LaunchCreateExtension; import google.registry.model.domain.launch.LaunchCreateExtension;
@ -39,6 +37,7 @@ import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
import google.registry.model.host.HostCommand; import google.registry.model.host.HostCommand;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.Type; import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.model.reporting.HistoryEntryDao;
import google.registry.xml.XmlException; import google.registry.xml.XmlException;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
@ -196,16 +195,10 @@ public class OteStats {
* <p>Stops when it notices that all tests have passed. * <p>Stops when it notices that all tests have passed.
*/ */
private OteStats recordRegistrarHistory(String registrarName) { private OteStats recordRegistrarHistory(String registrarName) {
ImmutableCollection<String> clientIds = ImmutableCollection<String> registrarIds =
OteAccountBuilder.createClientIdToTldMap(registrarName).keySet(); OteAccountBuilder.createClientIdToTldMap(registrarName).keySet();
Query<HistoryEntry> query = for (HistoryEntry historyEntry : HistoryEntryDao.loadHistoryObjectsByRegistrars(registrarIds)) {
ofy()
.load()
.type(HistoryEntry.class)
.filter("clientId in", clientIds)
.order("modificationTime");
for (HistoryEntry historyEntry : query) {
try { try {
record(historyEntry); record(historyEntry);
} catch (XmlException e) { } catch (XmlException e) {

View file

@ -21,7 +21,9 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
@ -30,7 +32,11 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import java.util.Comparator; import java.util.Comparator;
import java.util.stream.Stream;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /**
@ -85,21 +91,56 @@ public class HistoryEntryDao {
} }
} }
/** Loads all history objects from all time from the given registrars. */
public static Iterable<? extends HistoryEntry> loadHistoryObjectsByRegistrars(
ImmutableCollection<String> registrarIds) {
if (tm().isOfy()) {
return ofy()
.load()
.type(HistoryEntry.class)
.filter("clientId in", registrarIds)
.order("modificationTime");
} else {
return jpaTm()
.transact(
() ->
Streams.concat(
loadHistoryObjectFromSqlByRegistrars(ContactHistory.class, registrarIds),
loadHistoryObjectFromSqlByRegistrars(DomainHistory.class, registrarIds),
loadHistoryObjectFromSqlByRegistrars(HostHistory.class, registrarIds))
.sorted(Comparator.comparing(HistoryEntry::getModificationTime))
.collect(toImmutableList()));
}
}
private static Stream<? extends HistoryEntry> loadHistoryObjectFromSqlByRegistrars(
Class<? extends HistoryEntry> historyClass, ImmutableCollection<String> registrarIds) {
return jpaTm()
.getEntityManager()
.createQuery(
CriteriaQueryBuilder.create(historyClass)
.whereFieldIsIn("clientId", registrarIds)
.build())
.getResultStream();
}
private static Iterable<? extends HistoryEntry> loadHistoryObjectsForResourceFromSql( private static Iterable<? extends HistoryEntry> loadHistoryObjectsForResourceFromSql(
VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) { VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) {
// The class we're searching from is based on which parent type (e.g. Domain) we have
Class<? extends HistoryEntry> historyClass = getHistoryClassFromParent(parentKey.getKind()); Class<? extends HistoryEntry> historyClass = getHistoryClassFromParent(parentKey.getKind());
// The field representing repo ID unfortunately varies by history class
String repoIdFieldName = getRepoIdFieldNameFromHistoryClass(historyClass); String repoIdFieldName = getRepoIdFieldNameFromHistoryClass(historyClass);
String tableName = jpaTm().getEntityManager().getMetamodel().entity(historyClass).getName(); CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
String queryString = CriteriaQuery<? extends HistoryEntry> criteriaQuery =
String.format( CriteriaQueryBuilder.create(historyClass)
"SELECT entry FROM %s entry WHERE entry.modificationTime >= :afterTime AND " .where("modificationTime", criteriaBuilder::greaterThanOrEqualTo, afterTime)
+ "entry.modificationTime <= :beforeTime AND entry.%s = :parentKey", .where("modificationTime", criteriaBuilder::lessThanOrEqualTo, beforeTime)
tableName, repoIdFieldName); .where(repoIdFieldName, criteriaBuilder::equal, parentKey.getSqlKey().toString())
.build();
return jpaTm() return jpaTm()
.query(queryString, historyClass) .getEntityManager()
.setParameter("afterTime", afterTime) .createQuery(criteriaQuery)
.setParameter("beforeTime", beforeTime)
.setParameter("parentKey", parentKey.getSqlKey().toString())
.getResultStream() .getResultStream()
.sorted(Comparator.comparing(HistoryEntry::getModificationTime)) .sorted(Comparator.comparing(HistoryEntry::getModificationTime))
.collect(toImmutableList()); .collect(toImmutableList());
@ -127,15 +168,14 @@ public class HistoryEntryDao {
private static Iterable<? extends HistoryEntry> loadAllHistoryObjectsFromSql( private static Iterable<? extends HistoryEntry> loadAllHistoryObjectsFromSql(
Class<? extends HistoryEntry> historyClass, DateTime afterTime, DateTime beforeTime) { Class<? extends HistoryEntry> historyClass, DateTime afterTime, DateTime beforeTime) {
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
return jpaTm() return jpaTm()
.query( .getEntityManager()
String.format( .createQuery(
"SELECT entry FROM %s entry WHERE entry.modificationTime >= :afterTime AND " CriteriaQueryBuilder.create(historyClass)
+ "entry.modificationTime <= :beforeTime", .where("modificationTime", criteriaBuilder::greaterThanOrEqualTo, afterTime)
jpaTm().getEntityManager().getMetamodel().entity(historyClass).getName()), .where("modificationTime", criteriaBuilder::lessThanOrEqualTo, beforeTime)
historyClass) .build())
.setParameter("afterTime", afterTime)
.setParameter("beforeTime", beforeTime)
.getResultList(); .getResultList();
} }
} }

View file

@ -19,6 +19,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Streams.stream; import static com.google.common.collect.Streams.stream;
import static google.registry.model.ofy.ObjectifyService.ofy; 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.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import com.google.appengine.api.users.User; import com.google.appengine.api.users.User;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@ -316,30 +319,46 @@ public class AuthenticatedRegistrarAccessor {
logger.atInfo().log("Checking registrar contacts for user ID %s", user.getUserId()); logger.atInfo().log("Checking registrar contacts for user ID %s", user.getUserId());
// Find all registrars that have a registrar contact with this user's ID. // Find all registrars that have a registrar contact with this user's ID.
ImmutableList<Key<Registrar>> accessibleClientIds = if (tm().isOfy()) {
stream(ofy().load().type(RegistrarContact.class).filter("gaeUserId", user.getUserId())) ImmutableList<Key<Registrar>> accessibleClientIds =
.map(RegistrarContact::getParent) stream(ofy().load().type(RegistrarContact.class).filter("gaeUserId", user.getUserId()))
.collect(toImmutableList()); .map(RegistrarContact::getParent)
// Filter out disabled registrars (note that pending registrars still allow console login). .collect(toImmutableList());
ofy().load().keys(accessibleClientIds).values().stream() // Filter out disabled registrars (note that pending registrars still allow console login).
.filter(registrar -> registrar.getState() != State.DISABLED) ofy().load().keys(accessibleClientIds).values().stream()
.forEach(registrar -> builder.put(registrar.getClientId(), Role.OWNER)); .filter(registrar -> registrar.getState() != State.DISABLED)
.forEach(registrar -> builder.put(registrar.getClientId(), Role.OWNER));
} else {
jpaTm()
.transact(
() ->
jpaTm()
.query(
"SELECT r FROM Registrar r INNER JOIN RegistrarPoc rp ON "
+ "r.clientIdentifier = rp.registrarId WHERE rp.gaeUserId = "
+ ":gaeUserId AND r.state != :state",
Registrar.class)
.setParameter("gaeUserId", user.getUserId())
.setParameter("state", State.DISABLED)
.getResultStream()
.forEach(registrar -> builder.put(registrar.getClientId(), Role.OWNER)));
}
// Admins have ADMIN access to all registrars, and also OWNER access to the registry registrar // Admins have ADMIN access to all registrars, and also OWNER access to the registry registrar
// and all non-REAL or non-live registrars. // and all non-REAL or non-live registrars.
if (isAdmin) { if (isAdmin) {
ofy() transactIfJpaTm(
.load() () ->
.type(Registrar.class) tm().loadAllOf(Registrar.class)
.forEach( .forEach(
registrar -> { registrar -> {
if (registrar.getType() != Registrar.Type.REAL if (registrar.getType() != Registrar.Type.REAL
|| !registrar.isLive() || !registrar.isLive()
|| registrar.getClientId().equals(registryAdminClientId)) { || registrar.getClientId().equals(registryAdminClientId)) {
builder.put(registrar.getClientId(), Role.OWNER); builder.put(registrar.getClientId(), Role.OWNER);
} }
builder.put(registrar.getClientId(), Role.ADMIN); builder.put(registrar.getClientId(), Role.ADMIN);
}); }));
} }
return builder.build(); return builder.build();

View file

@ -15,19 +15,28 @@
package google.registry.model; package google.registry.model;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import google.registry.model.OteStats.StatType; import google.registry.model.OteStats.StatType;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import org.junit.jupiter.api.Test; import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
@DualDatabaseTest
public final class OteStatsTest { public final class OteStatsTest {
@RegisterExtension @RegisterExtension
public final AppEngineExtension appEngine = public final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build(); AppEngineExtension.builder().withDatastoreAndCloudSql().build();
@Test @BeforeEach
void beforeEach() {
createTld("tld");
}
@TestOfyAndSql
void testSuccess_allPass() throws Exception { void testSuccess_allPass() throws Exception {
OteStatsTestHelper.setupCompleteOte("blobio"); OteStatsTestHelper.setupCompleteOte("blobio");
OteStats stats = OteStats.getFromRegistrar("blobio"); OteStats stats = OteStats.getFromRegistrar("blobio");
@ -35,7 +44,7 @@ public final class OteStatsTest {
assertThat(stats.getSize()).isEqualTo(30); assertThat(stats.getSize()).isEqualTo(30);
} }
@Test @TestOfyAndSql
void testSuccess_incomplete() throws Exception { void testSuccess_incomplete() throws Exception {
OteStatsTestHelper.setupIncompleteOte("blobio"); OteStatsTestHelper.setupIncompleteOte("blobio");
OteStats stats = OteStats.getFromRegistrar("blobio"); OteStats stats = OteStats.getFromRegistrar("blobio");
@ -46,7 +55,7 @@ public final class OteStatsTest {
assertThat(stats.getSize()).isEqualTo(34); assertThat(stats.getSize()).isEqualTo(34);
} }
@Test @TestOfyAndSql
void testSuccess_toString() throws Exception { void testSuccess_toString() throws Exception {
OteStatsTestHelper.setupCompleteOte("blobio"); OteStatsTestHelper.setupCompleteOte("blobio");
OteStats stats = OteStats.getFromRegistrar("blobio"); OteStats stats = OteStats.getFromRegistrar("blobio");
@ -87,7 +96,7 @@ public final class OteStatsTest {
assertThat(stats.toString()).isEqualTo(expected); assertThat(stats.toString()).isEqualTo(expected);
} }
@Test @TestOfyAndSql
void testIncomplete_toString() throws Exception { void testIncomplete_toString() throws Exception {
OteStatsTestHelper.setupIncompleteOte("blobio"); OteStatsTestHelper.setupIncompleteOte("blobio");
OteStats stats = OteStats.getFromRegistrar("blobio"); OteStats stats = OteStats.getFromRegistrar("blobio");

View file

@ -14,126 +14,168 @@
package google.registry.model; package google.registry.model;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedHost;
import static google.registry.testing.DatabaseHelper.persistPremiumList; import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.TestDataHelper.loadBytes; import static google.registry.testing.TestDataHelper.loadBytes;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import google.registry.model.domain.DomainHistory;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.host.HostHistory;
import google.registry.model.reporting.HistoryEntry.Type; import google.registry.model.reporting.HistoryEntry.Type;
import java.io.IOException; import java.io.IOException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
public final class OteStatsTestHelper { public final class OteStatsTestHelper {
public static void setupCompleteOte(String baseClientId) throws IOException { public static void setupCompleteOte(String baseClientId) throws IOException {
setupIncompleteOte(baseClientId); setupIncompleteOte(baseClientId);
String oteAccount1 = String.format("%s-1", baseClientId); String oteAccount1 = String.format("%s-1", baseClientId);
DateTime now = DateTime.now(DateTimeZone.UTC);
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("xn--abc-873b2e7eb1k8a4lpjvv.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_idn.xml")) .setXmlBytes(getBytes("domain_create_idn.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_RESTORE) .setType(Type.DOMAIN_RESTORE)
.setXmlBytes(getBytes("domain_restore.xml")) .setXmlBytes(getBytes("domain_restore.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new HostHistory.Builder()
.setHostRepoId(persistDeletedHost("ns1.example.tld", now).getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.HOST_DELETE) .setType(Type.HOST_DELETE)
.setXmlBytes(getBytes("host_delete.xml")) .setXmlBytes(getBytes("host_delete.xml"))
.setModificationTime(now)
.build()); .build());
} }
/** /**
* Sets up an incomplete OT&E registrar. It is missing the following entries: * Sets up an incomplete OT&E registrar. It is missing the following entries:
* *
* - DOMAIN_CREATES_IDN * <ul>
* - DOMAIN_RESTORES * <li>DOMAIN_CREATES_IDN
* - HOST_DELETES * <li>DOMAIN_RESTORES
* <li>HOST_DELETES
* </ul>
* *
* TODO(b/122830156): Have this replicate the exact OT&E workflow with the correct client IDs * <p>TODO(b/122830156): Have this replicate the exact OT&E workflow with the correct client IDs
*/ */
public static void setupIncompleteOte(String baseClientId) throws IOException { public static void setupIncompleteOte(String baseClientId) throws IOException {
createTld("tld");
persistPremiumList("default_sandbox_list", "sandbox,USD 1000"); persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
OteAccountBuilder.forClientId(baseClientId).addContact("email@example.com").buildAndPersist(); OteAccountBuilder.forClientId(baseClientId).addContact("email@example.com").buildAndPersist();
String oteAccount1 = String.format("%s-1", baseClientId); String oteAccount1 = String.format("%s-1", baseClientId);
DateTime now = DateTime.now(DateTimeZone.UTC);
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("exampleone.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_sunrise.xml")) .setXmlBytes(getBytes("domain_create_sunrise.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example-one.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_claim_notice.xml")) .setXmlBytes(getBytes("domain_create_claim_notice.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_anchor_tenant_fee_standard.xml")) .setXmlBytes(getBytes("domain_create_anchor_tenant_fee_standard.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_dsdata.xml")) .setXmlBytes(getBytes("domain_create_dsdata.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistDeletedDomain("example.tld", now).getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_DELETE) .setType(Type.DOMAIN_DELETE)
.setXmlBytes(getBytes("domain_delete.xml")) .setXmlBytes(getBytes("domain_delete.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_APPROVE) .setType(Type.DOMAIN_TRANSFER_APPROVE)
.setXmlBytes(getBytes("domain_transfer_approve.xml")) .setXmlBytes(getBytes("domain_transfer_approve.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_CANCEL) .setType(Type.DOMAIN_TRANSFER_CANCEL)
.setXmlBytes(getBytes("domain_transfer_cancel.xml")) .setXmlBytes(getBytes("domain_transfer_cancel.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_REJECT) .setType(Type.DOMAIN_TRANSFER_REJECT)
.setXmlBytes(getBytes("domain_transfer_reject.xml")) .setXmlBytes(getBytes("domain_transfer_reject.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_REQUEST) .setType(Type.DOMAIN_TRANSFER_REQUEST)
.setXmlBytes(getBytes("domain_transfer_request.xml")) .setXmlBytes(getBytes("domain_transfer_request.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.DOMAIN_UPDATE) .setType(Type.DOMAIN_UPDATE)
.setXmlBytes(getBytes("domain_update_with_secdns.xml")) .setXmlBytes(getBytes("domain_update_with_secdns.xml"))
.setModificationTime(now)
.build()); .build());
persistResource( persistResource(
new HistoryEntry.Builder() new HostHistory.Builder()
.setHostRepoId(persistActiveHost("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.HOST_CREATE) .setType(Type.HOST_CREATE)
.setXmlBytes(getBytes("host_create_complete.xml")) .setXmlBytes(getBytes("host_create_complete.xml"))
.setModificationTime(now)
.build()); .build());
// Persist 10 host updates for a total of 25 history entries. Since these also sort last by // Persist 10 host updates for a total of 25 history entries. Since these also sort last by
// modification time, when these cause all tests to pass, only the first will be recorded and // modification time, when these cause all tests to pass, only the first will be recorded and
// the rest will be skipped. // the rest will be skipped.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
persistResource( persistResource(
new HistoryEntry.Builder() new HostHistory.Builder()
.setHostRepoId(persistActiveHost("example.tld").getRepoId())
.setClientId(oteAccount1) .setClientId(oteAccount1)
.setType(Type.HOST_UPDATE) .setType(Type.HOST_UPDATE)
.setXmlBytes(getBytes("host_update.xml")) .setXmlBytes(getBytes("host_update.xml"))

View file

@ -38,14 +38,15 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.Registrar.State;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException; import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.InjectExtension; import google.registry.testing.InjectExtension;
import google.registry.testing.TestOfyAndSql;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock; import org.mockito.Mock;
@ -56,6 +57,7 @@ import org.mockito.quality.Strictness;
/** Unit tests for {@link AuthenticatedRegistrarAccessor}. */ /** Unit tests for {@link AuthenticatedRegistrarAccessor}. */
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) @MockitoSettings(strictness = Strictness.LENIENT)
@DualDatabaseTest
class AuthenticatedRegistrarAccessorTest { class AuthenticatedRegistrarAccessorTest {
@RegisterExtension @RegisterExtension
@ -132,7 +134,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Users are owners for registrars if and only if they are in the contacts for that registrar. */ /** Users are owners for registrars if and only if they are in the contacts for that registrar. */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_user() { void getAllClientIdWithAccess_user() {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
new AuthenticatedRegistrarAccessor( new AuthenticatedRegistrarAccessor(
@ -144,7 +146,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Logged out users don't have access to anything. */ /** Logged out users don't have access to anything. */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_loggedOutUser() { void getAllClientIdWithAccess_loggedOutUser() {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
new AuthenticatedRegistrarAccessor( new AuthenticatedRegistrarAccessor(
@ -165,7 +167,7 @@ class AuthenticatedRegistrarAccessorTest {
* *
* <p>(in other words - they don't have OWNER access only to REAL registrars owned by others) * <p>(in other words - they don't have OWNER access only to REAL registrars owned by others)
*/ */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_gaeAdmin() { void getAllClientIdWithAccess_gaeAdmin() {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
new AuthenticatedRegistrarAccessor( new AuthenticatedRegistrarAccessor(
@ -197,7 +199,7 @@ class AuthenticatedRegistrarAccessorTest {
* *
* <p>(in other words - they don't have OWNER access only to REAL registrars owned by others) * <p>(in other words - they don't have OWNER access only to REAL registrars owned by others)
*/ */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_userInSupportGroup() { void getAllClientIdWithAccess_userInSupportGroup() {
when(groupsConnection.isMemberOfGroup("user@gmail.com", SUPPORT_GROUP.get())).thenReturn(true); when(groupsConnection.isMemberOfGroup("user@gmail.com", SUPPORT_GROUP.get())).thenReturn(true);
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
@ -220,7 +222,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Empty Support group email - skips check and doesn't generate the lazy. */ /** Empty Support group email - skips check and doesn't generate the lazy. */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_emptySupportEmail_works() { void getAllClientIdWithAccess_emptySupportEmail_works() {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
new AuthenticatedRegistrarAccessor( new AuthenticatedRegistrarAccessor(
@ -233,7 +235,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Support group check throws - continue anyway. */ /** Support group check throws - continue anyway. */
@Test @TestOfyAndSql
void getAllClientIdWithAccess_throwingGroupCheck_stillWorks() { void getAllClientIdWithAccess_throwingGroupCheck_stillWorks() {
when(groupsConnection.isMemberOfGroup(any(), any())).thenThrow(new RuntimeException("blah")); when(groupsConnection.isMemberOfGroup(any(), any())).thenThrow(new RuntimeException("blah"));
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
@ -247,7 +249,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Fail loading registrar if user doesn't have access to it. */ /** Fail loading registrar if user doesn't have access to it. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_noAccess_isNotAdmin() { void testGetRegistrarForUser_noAccess_isNotAdmin() {
expectGetRegistrarFailure( expectGetRegistrarFailure(
REAL_CLIENT_ID_WITHOUT_CONTACT, REAL_CLIENT_ID_WITHOUT_CONTACT,
@ -256,7 +258,7 @@ class AuthenticatedRegistrarAccessorTest {
verify(lazyGroupsConnection).get(); verify(lazyGroupsConnection).get();
} }
@Test @TestOfyAndSql
void testGetRegistrarForUser_registrarIsDisabled_isNotAdmin() { void testGetRegistrarForUser_registrarIsDisabled_isNotAdmin() {
persistResource( persistResource(
Registrar.loadByClientId("TheRegistrar") Registrar.loadByClientId("TheRegistrar")
@ -272,7 +274,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Fail loading registrar if user doesn't have access to it, even if it's not REAL. */ /** Fail loading registrar if user doesn't have access to it, even if it's not REAL. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_noAccess_isNotAdmin_notReal() { void testGetRegistrarForUser_noAccess_isNotAdmin_notReal() {
expectGetRegistrarFailure( expectGetRegistrarFailure(
OTE_CLIENT_ID_WITHOUT_CONTACT, OTE_CLIENT_ID_WITHOUT_CONTACT,
@ -282,7 +284,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Fail loading registrar if there's no user associated with the request. */ /** Fail loading registrar if there's no user associated with the request. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_noUser() { void testGetRegistrarForUser_noUser() {
expectGetRegistrarFailure( expectGetRegistrarFailure(
CLIENT_ID_WITH_CONTACT, CLIENT_ID_WITH_CONTACT,
@ -292,7 +294,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Succeed loading registrar if user has access to it. */ /** Succeed loading registrar if user has access to it. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_inContacts_isNotAdmin() throws Exception { void testGetRegistrarForUser_inContacts_isNotAdmin() throws Exception {
expectGetRegistrarSuccess( expectGetRegistrarSuccess(
CLIENT_ID_WITH_CONTACT, CLIENT_ID_WITH_CONTACT,
@ -302,7 +304,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Succeed loading registrar if admin with access. */ /** Succeed loading registrar if admin with access. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_inContacts_isAdmin() throws Exception { void testGetRegistrarForUser_inContacts_isAdmin() throws Exception {
expectGetRegistrarSuccess( expectGetRegistrarSuccess(
CLIENT_ID_WITH_CONTACT, CLIENT_ID_WITH_CONTACT,
@ -312,7 +314,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Succeed loading registrar for admin even if they aren't on the approved contacts list. */ /** Succeed loading registrar for admin even if they aren't on the approved contacts list. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_notInContacts_isAdmin() throws Exception { void testGetRegistrarForUser_notInContacts_isAdmin() throws Exception {
expectGetRegistrarSuccess( expectGetRegistrarSuccess(
REAL_CLIENT_ID_WITHOUT_CONTACT, REAL_CLIENT_ID_WITHOUT_CONTACT,
@ -321,7 +323,7 @@ class AuthenticatedRegistrarAccessorTest {
verifyNoInteractions(lazyGroupsConnection); verifyNoInteractions(lazyGroupsConnection);
} }
@Test @TestOfyAndSql
void testGetRegistrarForUser_registrarIsDisabled_isAdmin() throws Exception { void testGetRegistrarForUser_registrarIsDisabled_isAdmin() throws Exception {
persistResource( persistResource(
Registrar.loadByClientId("NewRegistrar") Registrar.loadByClientId("NewRegistrar")
@ -337,7 +339,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Succeed loading non-REAL registrar for admin. */ /** Succeed loading non-REAL registrar for admin. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_notInContacts_isAdmin_notReal() throws Exception { void testGetRegistrarForUser_notInContacts_isAdmin_notReal() throws Exception {
expectGetRegistrarSuccess( expectGetRegistrarSuccess(
OTE_CLIENT_ID_WITHOUT_CONTACT, OTE_CLIENT_ID_WITHOUT_CONTACT,
@ -347,7 +349,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** Fail loading registrar even if admin, if registrar doesn't exist. */ /** Fail loading registrar even if admin, if registrar doesn't exist. */
@Test @TestOfyAndSql
void testGetRegistrarForUser_doesntExist_isAdmin() { void testGetRegistrarForUser_doesntExist_isAdmin() {
expectGetRegistrarFailure( expectGetRegistrarFailure(
"BadClientId", "BadClientId",
@ -382,7 +384,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** guessClientIdForUser returns the first clientId in getAllClientIdWithRoles. */ /** guessClientIdForUser returns the first clientId in getAllClientIdWithRoles. */
@Test @TestOfyAndSql
void testGuessClientIdForUser_hasAccess_returnsFirst() throws Exception { void testGuessClientIdForUser_hasAccess_returnsFirst() throws Exception {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
AuthenticatedRegistrarAccessor.createForTesting( AuthenticatedRegistrarAccessor.createForTesting(
@ -395,7 +397,7 @@ class AuthenticatedRegistrarAccessorTest {
} }
/** If a user doesn't have access to any registrars, guess fails. */ /** If a user doesn't have access to any registrars, guess fails. */
@Test @TestOfyAndSql
void testGuessClientIdForUser_noAccess_fails() { void testGuessClientIdForUser_noAccess_fails() {
AuthenticatedRegistrarAccessor registrarAccessor = AuthenticatedRegistrarAccessor registrarAccessor =
AuthenticatedRegistrarAccessor.createForTesting(ImmutableSetMultimap.of()); AuthenticatedRegistrarAccessor.createForTesting(ImmutableSetMultimap.of());
@ -405,7 +407,7 @@ class AuthenticatedRegistrarAccessorTest {
.isEqualTo("TestUserId isn't associated with any registrar"); .isEqualTo("TestUserId isn't associated with any registrar");
} }
@Test @TestOfyAndSql
void testNullness() { void testNullness() {
new NullPointerTester() new NullPointerTester()
.setDefault(HttpServletRequest.class, req) .setDefault(HttpServletRequest.class, req)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before After
Before After