mirror of
https://github.com/google/nomulus
synced 2026-08-06 23:36:10 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74f0a8dd7b | ||
|
|
092e3dca47 | ||
|
|
b8a6ac72dd | ||
|
|
b602aac09a | ||
|
|
d86c002132 | ||
|
|
54c5a9450d | ||
|
|
0f0097c15c | ||
|
|
c9437d8c72 |
@@ -60,8 +60,9 @@ export class DomainListComponent {
|
||||
effect(() => {
|
||||
this.pageNumber = 0;
|
||||
this.totalResults = 0;
|
||||
this.reloadData();
|
||||
this.registrarService.registrarId();
|
||||
if (this.registrarService.registrarId()) {
|
||||
this.reloadData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -466,13 +466,10 @@ public class RdePipeline implements Serializable {
|
||||
// Contacts and hosts are only deposited in RDE, not BRDA.
|
||||
if (pendingDeposit.mode() == RdeMode.FULL) {
|
||||
HashSet<Serializable> contacts = new HashSet<>();
|
||||
contacts.add(domain.getAdminContact().getKey());
|
||||
contacts.add(domain.getTechContact().getKey());
|
||||
contacts.add(domain.getRegistrant().getKey());
|
||||
// Billing contact is not mandatory.
|
||||
if (domain.getBillingContact() != null) {
|
||||
contacts.add(domain.getBillingContact().getKey());
|
||||
}
|
||||
domain.getAdminContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getTechContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getRegistrant().ifPresent(c -> contacts.add(c.getKey()));
|
||||
domain.getBillingContact().ifPresent(c -> contacts.add(c.getKey()));
|
||||
referencedContactCounter.inc(contacts.size());
|
||||
contacts.forEach(
|
||||
contactRepoId ->
|
||||
|
||||
+8
@@ -96,6 +96,14 @@
|
||||
<max-retry-duration>3600s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that update membership in the console user group. -->
|
||||
<queue>
|
||||
<name>console-user-group-update</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>1</max-concurrent-dispatches>
|
||||
<max-retry-duration>3600s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for infrequent cron tasks (i.e. hourly or less often) that should retry three times on failure. -->
|
||||
<queue>
|
||||
<name>retryable-cron-tasks</name>
|
||||
|
||||
@@ -412,11 +412,13 @@ public class DomainFlowUtils {
|
||||
|
||||
/** Verify that no linked resources have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts, VKey<Contact> registrant, Set<VKey<Host>> nameservers)
|
||||
Set<DesignatedContact> contacts,
|
||||
Optional<VKey<Contact>> registrant,
|
||||
Set<VKey<Host>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
|
||||
registrant.ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
}
|
||||
@@ -480,9 +482,10 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresent(
|
||||
@Nullable VKey<Contact> registrant, Set<DesignatedContact> contacts)
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
if (registrant == null) {
|
||||
// TODO: Check minimum reg data set migration schedule here and don't throw when any are empty.
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
|
||||
@@ -498,14 +501,14 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
|
||||
static void validateRegistrantAllowedOnTld(String tld, Optional<String> registrantContactId)
|
||||
throws RegistrantNotAllowedException {
|
||||
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
|
||||
// Empty allow list or null registrantContactId are ignored.
|
||||
if (registrantContactId != null
|
||||
if (registrantContactId.isPresent()
|
||||
&& !allowedRegistrants.isEmpty()
|
||||
&& !allowedRegistrants.contains(registrantContactId)) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId);
|
||||
&& !allowedRegistrants.contains(registrantContactId.get())) {
|
||||
throw new RegistrantNotAllowedException(registrantContactId.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,8 +119,11 @@ public final class DomainInfoFlow implements TransactionalFlow {
|
||||
.setCreationTime(domain.getCreationTime())
|
||||
.setLastEppUpdateTime(domain.getLastEppUpdateTime())
|
||||
.setRegistrationExpirationTime(domain.getRegistrationExpirationTime())
|
||||
.setLastTransferTime(domain.getLastTransferTime())
|
||||
.setRegistrant(tm().loadByKey(domain.getRegistrant()).getContactId());
|
||||
.setLastTransferTime(domain.getLastTransferTime());
|
||||
domain
|
||||
.getRegistrant()
|
||||
.ifPresent(r -> infoBuilder.setRegistrant(tm().loadByKey(r).getContactId()));
|
||||
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid.
|
||||
if (registrarId.equals(domain.getCurrentSponsorRegistrarId()) || authInfo.isPresent()) {
|
||||
|
||||
@@ -85,6 +85,15 @@ public final class DomainPricingLogic {
|
||||
createFee = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
|
||||
} else {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken
|
||||
.get()
|
||||
.getRegistrationBehavior()
|
||||
.equals(RegistrationBehavior.NONPREMIUM_CREATE)) {
|
||||
domainPrices =
|
||||
DomainPrices.create(
|
||||
false, tld.getCreateBillingCost(dateTime), domainPrices.getRenewCost());
|
||||
}
|
||||
Money domainCreateCost =
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken);
|
||||
// Apply a sunrise discount if configured and applicable
|
||||
|
||||
@@ -278,7 +278,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setRegistrant(change.getRegistrant().or(domain::getRegistrant))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
@@ -301,7 +301,10 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
}
|
||||
|
||||
private static void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
if (change.getRegistrantContactId() != null && change.getRegistrantContactId().isEmpty()) {
|
||||
// TODO(mcilwain): Make this check the minimum registration data set migration schedule
|
||||
// and not require presence of a registrant in later stages.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
@@ -363,6 +364,33 @@ public class EntityYamlUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link FeatureStatus}. */
|
||||
public static class TimedTransitionPropertyFeatureStatusDeserializer
|
||||
extends StdDeserializer<TimedTransitionProperty<FeatureStatus>> {
|
||||
|
||||
public TimedTransitionPropertyFeatureStatusDeserializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public TimedTransitionPropertyFeatureStatusDeserializer(
|
||||
Class<TimedTransitionProperty<FeatureStatus>> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedTransitionProperty<FeatureStatus> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
natural(),
|
||||
DateTime::parse,
|
||||
key -> FeatureStatus.valueOf(valueMap.get(key)))));
|
||||
}
|
||||
}
|
||||
|
||||
/** A custom JSON deserializer for a {@link CreateAutoTimestamp}. */
|
||||
public static class CreateAutoTimestampDeserializer extends StdDeserializer<CreateAutoTimestamp> {
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -28,19 +29,28 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -75,8 +85,8 @@ public final class OteAccountBuilder {
|
||||
* Validation regex for registrar base client IDs (3-14 lowercase alphanumeric characters).
|
||||
*
|
||||
* <p>The base client ID is appended with numbers to create four different test registrar accounts
|
||||
* (e.g. reg-1, reg-3, reg-4, reg-5). Registrar client IDs are of type clIDType in eppcom.xsd
|
||||
* which is limited to 16 characters, hence the limit of 14 here to account for the dash and
|
||||
* (e.g., reg-1, reg-3, reg-4, reg-5). Registrar client IDs are of type clIDType in eppcom.xsd
|
||||
* that is limited to 16 characters, hence the limit of 14 here to account for the dash and
|
||||
* numbers.
|
||||
*
|
||||
* <p>The base client ID is also used to generate the OT&E TLDs, hence the restriction to
|
||||
@@ -113,7 +123,7 @@ public final class OteAccountBuilder {
|
||||
* The default billing account map applied to all OT&E registrars.
|
||||
*
|
||||
* <p>This contains dummy values for USD and JPY so that OT&E registrars can be granted access
|
||||
* to all existing TLDs in sandbox. Note that OT&E is only on sandbox and thus these dummy
|
||||
* to all existing TLDs in sandbox. Note that OT&E is only on sandbox, and thus these dummy
|
||||
* values will never be used in production (the only environment where real invoicing takes
|
||||
* place).
|
||||
*/
|
||||
@@ -124,7 +134,7 @@ public final class OteAccountBuilder {
|
||||
private final Tld sunriseTld;
|
||||
private final Tld gaTld;
|
||||
private final Tld eapTld;
|
||||
private final ImmutableList.Builder<RegistrarPoc> contactsBuilder = new ImmutableList.Builder<>();
|
||||
private final List<User> users = new ArrayList<>();
|
||||
|
||||
private ImmutableList<Registrar> registrars;
|
||||
private boolean replaceExisting = false;
|
||||
@@ -172,16 +182,28 @@ public final class OteAccountBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a RegistrarContact with Web Console access.
|
||||
* Adds a {@link User} with Web Console access.
|
||||
*
|
||||
* <p>NOTE: can be called more than once, adding multiple contacts. Each contact will have access
|
||||
* to all OT&E Registrars.
|
||||
* <p>NOTE: can be called more than once, adding multiple users. Each user will have access to all
|
||||
* OT&E Registrars.
|
||||
*
|
||||
* @param email the contact/login email that will have web-console access to all the Registrars.
|
||||
* Must be from "our G Suite domain".
|
||||
* @param email the login email that will have web-console access to all the Registrars. Must be
|
||||
* from "our Google Workspace domain".
|
||||
*/
|
||||
public OteAccountBuilder addContact(String email) {
|
||||
registrars.forEach(registrar -> contactsBuilder.add(createRegistrarContact(email, registrar)));
|
||||
public OteAccountBuilder addUser(String email) {
|
||||
users.add(
|
||||
new User.Builder()
|
||||
.setEmailAddress(email)
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
registrars.stream()
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
Registrar::getRegistrarId,
|
||||
registrar -> RegistrarRole.ACCOUNT_MANAGER)))
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -217,7 +239,7 @@ public final class OteAccountBuilder {
|
||||
return transformRegistrars(builder -> builder.setClientCertificate(asciiCert, now));
|
||||
}
|
||||
|
||||
/** Sets the IP allow list to all the OT&E Registrars. */
|
||||
/** Sets the IP allowlist to all the OT&E Registrars. */
|
||||
public OteAccountBuilder setIpAllowList(Collection<String> ipAllowList) {
|
||||
ImmutableList<CidrAddressBlock> ipAddressAllowList =
|
||||
ipAllowList.stream().map(CidrAddressBlock::create).collect(toImmutableList());
|
||||
@@ -237,18 +259,37 @@ public final class OteAccountBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map from the OT&E registrarIds we will create to the new TLDs they will have access
|
||||
* to.
|
||||
* Return the map from the OT&E registrarIds we will create to the new TLDs they will have
|
||||
* access to.
|
||||
*/
|
||||
public ImmutableMap<String, String> getRegistrarIdToTldMap() {
|
||||
return registrarIdToTld;
|
||||
}
|
||||
|
||||
/** Grants the users permission to pass IAP. */
|
||||
public void grantIapPermission(
|
||||
Optional<String> groupEmailAddress, CloudTasksUtils cloudTasksUtils, IamClient iamClient) {
|
||||
for (User user : users) {
|
||||
User.grantIapPermission(
|
||||
user.getEmailAddress(), groupEmailAddress, cloudTasksUtils, iamClient);
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves all the OT&E entities we created. */
|
||||
private void saveAllEntities() {
|
||||
// use ImmutableObject instead of Registry so that the Key generation doesn't break
|
||||
ImmutableList<Tld> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
|
||||
ImmutableList<RegistrarPoc> contacts = contactsBuilder.build();
|
||||
Map<String, User> existingUsers = new HashMap<>();
|
||||
|
||||
users.forEach(
|
||||
user ->
|
||||
UserDao.loadUser(user.getEmailAddress())
|
||||
.ifPresent(
|
||||
existingUser ->
|
||||
existingUsers.put(existingUser.getEmailAddress(), existingUser)));
|
||||
|
||||
if (!replaceExisting) {
|
||||
checkState(existingUsers.isEmpty(), "Found existing users: %s", existingUsers);
|
||||
}
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
@@ -256,8 +297,7 @@ public final class OteAccountBuilder {
|
||||
ImmutableList<VKey<? extends ImmutableObject>> keys =
|
||||
Streams.concat(
|
||||
registries.stream().map(tld -> Tld.createVKey(tld.getTldStr())),
|
||||
registrars.stream().map(Registrar::createVKey),
|
||||
contacts.stream().map(RegistrarPoc::createVKey))
|
||||
registrars.stream().map(Registrar::createVKey))
|
||||
.collect(toImmutableList());
|
||||
ImmutableMap<VKey<? extends ImmutableObject>, ImmutableObject> existingObjects =
|
||||
tm().loadByKeysIfPresent(keys);
|
||||
@@ -275,8 +315,18 @@ public final class OteAccountBuilder {
|
||||
registrars = registrars.stream().map(this::addAllowedTld).collect(toImmutableList());
|
||||
// and we can save the registrars and contacts!
|
||||
tm().putAll(registrars);
|
||||
tm().putAll(contacts);
|
||||
});
|
||||
|
||||
for (User user : users) {
|
||||
String email = user.getEmailAddress();
|
||||
if (existingUsers.containsKey(email)) {
|
||||
// Note that other roles for the existing user are reset. We do this instead of simply
|
||||
// saving the new user is that UserDao does not allow us to save the new user with the same
|
||||
// email as the existing user.
|
||||
user = existingUsers.get(email).asBuilder().setUserRoles(user.getUserRoles()).build();
|
||||
}
|
||||
UserDao.saveUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
private Registrar addAllowedTld(Registrar registrar) {
|
||||
@@ -336,15 +386,6 @@ public final class OteAccountBuilder {
|
||||
.build();
|
||||
}
|
||||
|
||||
private static RegistrarPoc createRegistrarContact(String email, Registrar registrar) {
|
||||
return new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName(email)
|
||||
.setEmailAddress(email)
|
||||
.setLoginEmailAddress(email)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Returns the registrar IDs of the OT&E, with the TLDs each has access to. */
|
||||
public static ImmutableMap<String, String> createRegistrarIdToTldMap(String baseRegistrarId) {
|
||||
checkArgument(
|
||||
|
||||
@@ -14,29 +14,34 @@
|
||||
|
||||
package google.registry.model.common;
|
||||
|
||||
import static com.google.api.client.util.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyFeatureStatusDeserializer;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -54,60 +59,76 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
public enum FeatureName {
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
@Id String featureName;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Id
|
||||
FeatureName featureName;
|
||||
|
||||
/** A map of times for each {@link FeatureStatus} the FeatureFlag should hold. */
|
||||
@Column(nullable = false)
|
||||
@JsonDeserialize(using = TimedTransitionPropertyFeatureStatusDeserializer.class)
|
||||
TimedTransitionProperty<FeatureStatus> status =
|
||||
TimedTransitionProperty.withInitialValue(FeatureStatus.INACTIVE);
|
||||
|
||||
public static FeatureFlag get(String featureName) {
|
||||
FeatureFlag maybeFeatureFlag = CACHE.get(featureName);
|
||||
if (maybeFeatureFlag == null) {
|
||||
throw new FeatureFlagNotFoundException(featureName);
|
||||
} else {
|
||||
return maybeFeatureFlag;
|
||||
}
|
||||
public static Optional<FeatureFlag> getUncached(FeatureName featureName) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(createVKey(featureName)));
|
||||
}
|
||||
|
||||
public static ImmutableSet<FeatureFlag> get(Set<String> featureNames) {
|
||||
Map<String, FeatureFlag> featureFlags = CACHE.getAll(featureNames);
|
||||
ImmutableSet<String> missingFlags =
|
||||
Sets.difference(featureNames, featureFlags.keySet()).immutableCopy();
|
||||
public static ImmutableList<FeatureFlag> getAllUncached() {
|
||||
return tm().transact(() -> tm().loadAllOf(FeatureFlag.class));
|
||||
}
|
||||
|
||||
public static FeatureFlag get(FeatureName featureName) {
|
||||
Optional<FeatureFlag> maybeFeatureFlag = CACHE.get(featureName);
|
||||
return maybeFeatureFlag.orElseThrow(() -> new FeatureFlagNotFoundException(featureName));
|
||||
}
|
||||
|
||||
public static ImmutableSet<FeatureFlag> getAll(Set<FeatureName> featureNames) {
|
||||
Map<FeatureName, Optional<FeatureFlag>> featureFlags = CACHE.getAll(featureNames);
|
||||
ImmutableSet<FeatureName> missingFlags =
|
||||
featureFlags.entrySet().stream()
|
||||
.filter(e -> e.getValue().isEmpty())
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(toImmutableSet());
|
||||
if (missingFlags.isEmpty()) {
|
||||
return featureFlags.values().stream().collect(toImmutableSet());
|
||||
return featureFlags.values().stream().map(Optional::get).collect(toImmutableSet());
|
||||
} else {
|
||||
throw new FeatureFlagNotFoundException(missingFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/** A cache that loads the {@link FeatureFlag} for a given featureName. */
|
||||
private static final LoadingCache<String, FeatureFlag> CACHE =
|
||||
private static final LoadingCache<FeatureName, Optional<FeatureFlag>> CACHE =
|
||||
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public FeatureFlag load(final String featureName) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(featureName)))
|
||||
.orElse(null);
|
||||
public Optional<FeatureFlag> load(final FeatureName featureName) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(featureName)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends String, ? extends FeatureFlag> loadAll(
|
||||
Set<? extends String> featureFlagNames) {
|
||||
ImmutableMap<String, VKey<FeatureFlag>> keysMap =
|
||||
public Map<? extends FeatureName, ? extends Optional<FeatureFlag>> loadAll(
|
||||
Set<? extends FeatureName> featureFlagNames) {
|
||||
ImmutableMap<FeatureName, VKey<FeatureFlag>> keysMap =
|
||||
featureFlagNames.stream()
|
||||
.collect(
|
||||
toImmutableMap(featureName -> featureName, FeatureFlag::createVKey));
|
||||
Map<VKey<? extends FeatureFlag>, FeatureFlag> entities =
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return entities.values().stream()
|
||||
.collect(toImmutableMap(flag -> flag.featureName, flag -> flag));
|
||||
return Maps.toMap(
|
||||
featureFlagNames,
|
||||
name -> Optional.ofNullable(entities.get(createVKey(name))));
|
||||
}
|
||||
});
|
||||
|
||||
public static VKey<FeatureFlag> createVKey(String featureName) {
|
||||
public static VKey<FeatureFlag> createVKey(FeatureName featureName) {
|
||||
return VKey.create(FeatureFlag.class, featureName);
|
||||
}
|
||||
|
||||
@@ -116,10 +137,11 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return createVKey(featureName);
|
||||
}
|
||||
|
||||
public String getFeatureName() {
|
||||
public FeatureName getFeatureName() {
|
||||
return featureName;
|
||||
}
|
||||
|
||||
@JsonProperty("status")
|
||||
public TimedTransitionProperty<FeatureStatus> getStatusMap() {
|
||||
return status;
|
||||
}
|
||||
@@ -144,20 +166,17 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
|
||||
@Override
|
||||
public FeatureFlag build() {
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(getInstance().featureName),
|
||||
"Feature name must not be null or empty");
|
||||
getInstance().status.checkValidity();
|
||||
checkArgument(getInstance().featureName != null, "FeatureName cannot be null");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setFeatureName(String featureName) {
|
||||
checkState(getInstance().featureName == null, "Feature name can only be set once");
|
||||
public Builder setFeatureName(FeatureName featureName) {
|
||||
getInstance().featureName = featureName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStatus(ImmutableSortedMap<DateTime, FeatureStatus> statusMap) {
|
||||
public Builder setStatusMap(ImmutableSortedMap<DateTime, FeatureStatus> statusMap) {
|
||||
getInstance().status = TimedTransitionProperty.fromValueMap(statusMap);
|
||||
return this;
|
||||
}
|
||||
@@ -166,11 +185,11 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
/** Exception to throw when no FeatureFlag entity is found for given FeatureName string(s). */
|
||||
public static class FeatureFlagNotFoundException extends RuntimeException {
|
||||
|
||||
FeatureFlagNotFoundException(ImmutableSet<String> featureNames) {
|
||||
FeatureFlagNotFoundException(ImmutableSet<FeatureName> featureNames) {
|
||||
super("No feature flag object(s) found for " + Joiner.on(", ").join(featureNames));
|
||||
}
|
||||
|
||||
FeatureFlagNotFoundException(String featureName) {
|
||||
public FeatureFlagNotFoundException(FeatureName featureName) {
|
||||
this(ImmutableSet.of(featureName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,19 @@
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static google.registry.tools.server.UpdateUserGroupAction.GROUP_UPDATE_QUEUE;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import google.registry.tools.server.UpdateUserGroupAction.Mode;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -31,6 +43,76 @@ import javax.persistence.Table;
|
||||
@Table(indexes = {@Index(columnList = "emailAddress", name = "user_email_address_idx")})
|
||||
public class User extends UserBase {
|
||||
|
||||
public static final String IAP_SECURED_WEB_APP_USER_ROLE = "roles/iap.httpsResourceAccessor";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* Grants the user permission to pass IAP.
|
||||
*
|
||||
* <p>Depending on if a console user group is set up, the permission is granted either
|
||||
* individually or via group membership.
|
||||
*/
|
||||
public static void grantIapPermission(
|
||||
String emailAddress,
|
||||
Optional<String> groupEmailAddress,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
IamClient iamClient) {
|
||||
if (RegistryEnvironment.isInTestServer()) {
|
||||
return;
|
||||
}
|
||||
if (groupEmailAddress.isEmpty()) {
|
||||
logger.atInfo().log("Granting IAP role to user %s", emailAddress);
|
||||
iamClient.addBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
logger.atInfo().log("Adding %s to group %s", emailAddress, groupEmailAddress.get());
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, UpdateUserGroupAction.Mode.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke the user's permission to pass IAP.
|
||||
*
|
||||
* <p>Depending on if a console user group is set up, the permission is revoked either
|
||||
* individually or via group membership.
|
||||
*/
|
||||
public static void revokeIapPermission(
|
||||
String emailAddress,
|
||||
Optional<String> groupEmailAddress,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
IamClient iamClient) {
|
||||
if (RegistryEnvironment.isInTestServer()) {
|
||||
return;
|
||||
}
|
||||
if (groupEmailAddress.isEmpty()) {
|
||||
logger.atInfo().log("Removing IAP role from user %s", emailAddress);
|
||||
iamClient.removeBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
logger.atInfo().log("Removing %s from group %s", emailAddress, groupEmailAddress.get());
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void modifyGroupMembershipAsync(
|
||||
String userEmailAddress,
|
||||
String groupEmailAddress,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
Mode mode) {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask(
|
||||
UpdateUserGroupAction.PATH,
|
||||
Service.TOOLS,
|
||||
ImmutableMultimap.of(
|
||||
"userEmailAddress",
|
||||
userEmailAddress,
|
||||
"groupEmailAddress",
|
||||
groupEmailAddress,
|
||||
"groupUpdateMode",
|
||||
mode.name()));
|
||||
cloudTasksUtils.enqueue(GROUP_UPDATE_QUEUE, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
@@ -46,6 +46,7 @@ import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
@@ -131,11 +132,11 @@ public class DomainBase extends EppResource
|
||||
@Expose @Transient Set<VKey<Host>> nsHosts;
|
||||
|
||||
/** Contacts. */
|
||||
@Expose VKey<Contact> adminContact;
|
||||
@Expose @Nullable VKey<Contact> adminContact;
|
||||
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
@Expose @Nullable VKey<Contact> billingContact;
|
||||
@Expose @Nullable VKey<Contact> techContact;
|
||||
@Expose @Nullable VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@@ -585,32 +586,49 @@ public class DomainBase extends EppResource
|
||||
}
|
||||
|
||||
/** A key to the registrant who registered this domain. */
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrantContact;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrantContact);
|
||||
}
|
||||
|
||||
public VKey<Contact> getAdminContact() {
|
||||
return adminContact;
|
||||
public Optional<VKey<Contact>> getAdminContact() {
|
||||
return Optional.ofNullable(adminContact);
|
||||
}
|
||||
|
||||
public VKey<Contact> getBillingContact() {
|
||||
return billingContact;
|
||||
public Optional<VKey<Contact>> getBillingContact() {
|
||||
return Optional.ofNullable(billingContact);
|
||||
}
|
||||
|
||||
public VKey<Contact> getTechContact() {
|
||||
return techContact;
|
||||
public Optional<VKey<Contact>> getTechContact() {
|
||||
return Optional.ofNullable(techContact);
|
||||
}
|
||||
|
||||
/** Associated contacts for the domain (other than registrant). */
|
||||
/**
|
||||
* Associated contacts for the domain (other than registrant).
|
||||
*
|
||||
* <p>Note: This can be an empty set if no contacts are present for the domain.
|
||||
*/
|
||||
public ImmutableSet<DesignatedContact> getContacts() {
|
||||
return getAllContacts(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all associated contacts for the domain, including the registrant.
|
||||
*
|
||||
* <p>Note: This can be an empty set if no contacts are present for the domain.
|
||||
*/
|
||||
public ImmutableSet<DesignatedContact> getAllContacts() {
|
||||
return getAllContacts(true);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
return authInfo;
|
||||
}
|
||||
|
||||
/** Returns all referenced contacts from this domain. */
|
||||
/**
|
||||
* Returns all referenced contacts from this domain.
|
||||
*
|
||||
* <p>Note: This can be an empty set if no contacts are present for the domain.
|
||||
*/
|
||||
public ImmutableSet<VKey<Contact>> getReferencedContacts() {
|
||||
return nullToEmptyImmutableCopy(getAllContacts(true)).stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
@@ -620,18 +638,12 @@ public class DomainBase extends EppResource
|
||||
|
||||
private ImmutableSet<DesignatedContact> getAllContacts(boolean includeRegistrant) {
|
||||
ImmutableSet.Builder<DesignatedContact> builder = new ImmutableSet.Builder<>();
|
||||
if (includeRegistrant && registrantContact != null) {
|
||||
builder.add(DesignatedContact.create(DesignatedContact.Type.REGISTRANT, registrantContact));
|
||||
}
|
||||
if (adminContact != null) {
|
||||
builder.add(DesignatedContact.create(DesignatedContact.Type.ADMIN, adminContact));
|
||||
}
|
||||
if (billingContact != null) {
|
||||
builder.add(DesignatedContact.create(DesignatedContact.Type.BILLING, billingContact));
|
||||
}
|
||||
if (techContact != null) {
|
||||
builder.add(DesignatedContact.create(DesignatedContact.Type.TECH, techContact));
|
||||
if (includeRegistrant) {
|
||||
getRegistrant().ifPresent(c -> builder.add(DesignatedContact.create(Type.REGISTRANT, c)));
|
||||
}
|
||||
getAdminContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.ADMIN, c)));
|
||||
getBillingContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.BILLING, c)));
|
||||
getTechContact().ifPresent(c -> builder.add(DesignatedContact.create(Type.TECH, c)));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -647,11 +659,13 @@ public class DomainBase extends EppResource
|
||||
*/
|
||||
void setContactFields(Set<DesignatedContact> contacts, boolean includeRegistrant) {
|
||||
// Set the individual contact fields.
|
||||
billingContact = techContact = adminContact = null;
|
||||
billingContact = null;
|
||||
techContact = null;
|
||||
adminContact = null;
|
||||
if (includeRegistrant) {
|
||||
registrantContact = null;
|
||||
}
|
||||
HashSet<DesignatedContact.Type> contactsDiscovered = new HashSet<>();
|
||||
HashSet<Type> contactsDiscovered = new HashSet<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
checkArgument(
|
||||
!contactsDiscovered.contains(contact.getType()),
|
||||
@@ -682,7 +696,7 @@ public class DomainBase extends EppResource
|
||||
|
||||
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
|
||||
static final Predicate<DesignatedContact> IS_REGISTRANT =
|
||||
(DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type);
|
||||
(DesignatedContact contact) -> Type.REGISTRANT.equals(contact.type);
|
||||
|
||||
/** An override of {@link EppResource#asBuilder} with tighter typing. */
|
||||
@Override
|
||||
@@ -717,7 +731,6 @@ public class DomainBase extends EppResource
|
||||
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
|
||||
|
||||
checkArgumentNotNull(emptyToNull(instance.domainName), "Missing domainName");
|
||||
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
|
||||
instance.tld = getTldFromDomainName(instance.domainName);
|
||||
|
||||
T newDomain = super.build();
|
||||
@@ -749,9 +762,9 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setRegistrant(VKey<Contact> registrant) {
|
||||
public B setRegistrant(Optional<VKey<Contact>> registrant) {
|
||||
// Set the registrant field specifically.
|
||||
getInstance().registrantContact = registrant;
|
||||
getInstance().registrantContact = registrant.orElse(null);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
@@ -76,21 +77,21 @@ public class DomainCommand {
|
||||
|
||||
/** The contactId of the registrant who registered this domain. */
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
String registrantContactId;
|
||||
|
||||
/** A resolved key to the registrant who registered this domain. */
|
||||
@XmlTransient VKey<Contact> registrant;
|
||||
@Nullable @XmlTransient VKey<Contact> registrant;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
DomainAuthInfo authInfo;
|
||||
|
||||
public String getRegistrantContactId() {
|
||||
return registrantContactId;
|
||||
public Optional<String> getRegistrantContactId() {
|
||||
return Optional.ofNullable(registrantContactId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VKey<Contact> getRegistrant() {
|
||||
return registrant;
|
||||
public Optional<VKey<Contact>> getRegistrant() {
|
||||
return Optional.ofNullable(registrant);
|
||||
}
|
||||
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
|
||||
@@ -63,6 +63,7 @@ public abstract class DomainInfoData implements ResponseData {
|
||||
abstract ImmutableSet<StatusValue> getStatusValues();
|
||||
|
||||
@XmlElement(name = "registrant")
|
||||
@Nullable
|
||||
abstract String getRegistrant();
|
||||
|
||||
@XmlElement(name = "contact")
|
||||
|
||||
@@ -50,6 +50,7 @@ import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import google.registry.persistence.converter.JodaMoneyType;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -63,6 +64,9 @@ import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** An entity representing an allocation token. */
|
||||
@@ -115,7 +119,17 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
*/
|
||||
BYPASS_TLD_STATE,
|
||||
/** Bypasses most checks and creates the domain as an anchor tenant, with all that implies. */
|
||||
ANCHOR_TENANT
|
||||
ANCHOR_TENANT,
|
||||
/**
|
||||
* Bypasses the premium list to use the standard creation price. Does not affect the renewal
|
||||
* price.
|
||||
*
|
||||
* <p>This cannot be specified along with a discount fraction, and any renewals (automatic or
|
||||
* otherwise) will use the premium price for the domain if one exists.
|
||||
*
|
||||
* <p>Tokens with this behavior must be tied to a single particular domain.
|
||||
*/
|
||||
NONPREMIUM_CREATE
|
||||
}
|
||||
|
||||
/** Type of the token that indicates how and where it should be used. */
|
||||
@@ -226,6 +240,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
/** The price used for renewals iff the renewalPriceBehavior is SPECIFIED. */
|
||||
@Nullable
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(columns = {@Column(name = "renewalPriceAmount"), @Column(name = "renewalPriceCurrency")})
|
||||
Money renewalPrice;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
|
||||
@@ -300,6 +320,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public Optional<Money> getRenewalPrice() {
|
||||
return Optional.ofNullable(renewalPrice);
|
||||
}
|
||||
|
||||
public RegistrationBehavior getRegistrationBehavior() {
|
||||
return registrationBehavior;
|
||||
}
|
||||
@@ -367,29 +391,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
public AllocationToken build() {
|
||||
checkArgumentNotNull(getInstance().tokenType, "Token type must be specified");
|
||||
checkArgument(!Strings.isNullOrEmpty(getInstance().token), "Token must not be null or empty");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|
||||
|| getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"Bulk tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|
||||
|| ImmutableSet.of(CommandName.CREATE).equals(getInstance().allowedEppActions),
|
||||
"Bulk tokens may only be valid for CREATE actions");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|
||||
|| !getInstance().discountPremiums,
|
||||
"Bulk tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().domainName == null || getInstance().tokenType.isOneTimeUse(),
|
||||
"Domain name can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||
checkArgument(
|
||||
getInstance().redemptionHistoryId == null || getInstance().tokenType.isOneTimeUse(),
|
||||
"Redemption history entry can only be specified for SINGLE_USE or REGISTER_BSA tokens");
|
||||
checkArgument(
|
||||
getInstance().tokenType != TokenType.BULK_PRICING
|
||||
|| (getInstance().allowedClientIds != null
|
||||
&& getInstance().allowedClientIds.size() == 1),
|
||||
"BULK_PRICING tokens must have exactly one allowed client registrar");
|
||||
checkArgument(
|
||||
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
|
||||
"Discount premiums can only be specified along with a discount fraction");
|
||||
@@ -404,6 +411,44 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain");
|
||||
}
|
||||
if (getInstance().registrationBehavior.equals(RegistrationBehavior.NONPREMIUM_CREATE)) {
|
||||
checkArgument(
|
||||
getInstance().discountFraction == 0.0,
|
||||
"NONPREMIUM_CREATE tokens cannot apply a discount");
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "NONPREMIUM_CREATE tokens must be tied to a domain");
|
||||
checkArgument(
|
||||
getInstance().allowedEppActions == null
|
||||
|| getInstance().allowedEppActions.contains(CommandName.CREATE),
|
||||
"NONPREMIUM_CREATE tokens must allow for CREATE actions");
|
||||
}
|
||||
|
||||
checkArgument(
|
||||
getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED)
|
||||
== (getInstance().renewalPrice != null),
|
||||
"renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
|
||||
|
||||
if (getInstance().tokenType.equals(TokenType.BULK_PRICING)) {
|
||||
checkArgument(
|
||||
getInstance().discountFraction == 1.0,
|
||||
"BULK_PRICING tokens must have a discountFraction of 1.0");
|
||||
checkArgument(
|
||||
!getInstance().shouldDiscountPremiums(),
|
||||
"BULK_PRICING tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"BULK_PRICING tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
checkArgument(
|
||||
getInstance().renewalPrice.getAmount().intValue() == 0,
|
||||
"BULK_PRICING tokens must have a renewal price of 0");
|
||||
checkArgument(
|
||||
ImmutableSet.of(CommandName.CREATE).equals(getInstance().allowedEppActions),
|
||||
"BULK_PRICING tokens may only be valid for CREATE actions");
|
||||
checkArgument(
|
||||
getInstance().allowedClientIds != null && getInstance().allowedClientIds.size() == 1,
|
||||
"BULK_PRICING tokens must have exactly one allowed client registrar");
|
||||
}
|
||||
|
||||
if (getInstance().domainName != null) {
|
||||
try {
|
||||
DomainFlowUtils.validateDomainName(getInstance().domainName);
|
||||
@@ -500,6 +545,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPrice(Money renewalPrice) {
|
||||
getInstance().renewalPrice = renewalPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistrationBehavior(RegistrationBehavior registrationBehavior) {
|
||||
getInstance().registrationBehavior = registrationBehavior;
|
||||
return this;
|
||||
|
||||
@@ -46,7 +46,7 @@ public interface PremiumPricingEngine {
|
||||
private Money createCost;
|
||||
private Money renewCost;
|
||||
|
||||
static DomainPrices create(boolean isPremium, Money createCost, Money renewCost) {
|
||||
public static DomainPrices create(boolean isPremium, Money createCost, Money renewCost) {
|
||||
DomainPrices instance = new DomainPrices();
|
||||
instance.isPremium = isPremium;
|
||||
instance.createCost = createCost;
|
||||
|
||||
@@ -41,9 +41,9 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
@@ -192,21 +192,19 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
|
||||
/** Returns the TLD for a given TLD, throwing if none exists. */
|
||||
public static Tld get(String tld) {
|
||||
Tld maybeTld = CACHE.get(tld);
|
||||
if (maybeTld == null) {
|
||||
throw new TldNotFoundException(tld);
|
||||
} else {
|
||||
return maybeTld;
|
||||
}
|
||||
return CACHE.get(tld).orElseThrow(() -> new TldNotFoundException(tld));
|
||||
}
|
||||
|
||||
/** Returns the TLD entities for the given TLD strings, throwing if any don't exist. */
|
||||
public static ImmutableSet<Tld> get(Set<String> tlds) {
|
||||
Map<String, Tld> registries = CACHE.getAll(tlds);
|
||||
Map<String, Optional<Tld>> registries = CACHE.getAll(tlds);
|
||||
ImmutableSet<String> missingRegistries =
|
||||
Sets.difference(tlds, registries.keySet()).immutableCopy();
|
||||
registries.entrySet().stream()
|
||||
.filter(e -> e.getValue().isEmpty())
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(toImmutableSet());
|
||||
if (missingRegistries.isEmpty()) {
|
||||
return registries.values().stream().collect(toImmutableSet());
|
||||
return registries.values().stream().map(Optional::get).collect(toImmutableSet());
|
||||
} else {
|
||||
throw new TldNotFoundException(missingRegistries);
|
||||
}
|
||||
@@ -224,24 +222,24 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
/** A cache that loads the {@link Tld} for a given tld. */
|
||||
private static final LoadingCache<String, Tld> CACHE =
|
||||
private static final LoadingCache<String, Optional<Tld>> CACHE =
|
||||
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<>() {
|
||||
@Override
|
||||
public Tld load(final String tld) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(tld)))
|
||||
.orElse(null);
|
||||
public Optional<Tld> load(final String tld) {
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(tld)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends String, ? extends Tld> loadAll(Set<? extends String> tlds) {
|
||||
public Map<? extends String, ? extends Optional<Tld>> loadAll(
|
||||
Set<? extends String> tlds) {
|
||||
ImmutableMap<String, VKey<Tld>> keysMap =
|
||||
tlds.stream().collect(toImmutableMap(tld -> tld, Tld::createVKey));
|
||||
Map<VKey<? extends Tld>, Tld> entities =
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return entities.values().stream()
|
||||
.collect(toImmutableMap(tld -> tld.tldStr, tld -> tld));
|
||||
return Maps.toMap(
|
||||
tlds, tld -> Optional.ofNullable(entities.get(createVKey(tld))));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
@@ -191,6 +192,8 @@ interface RequestComponent {
|
||||
|
||||
ConsoleRegistryLockAction consoleRegistryLockAction();
|
||||
|
||||
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
|
||||
|
||||
ConsoleUiAction consoleUiAction();
|
||||
|
||||
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
@@ -69,6 +70,8 @@ public interface FrontendRequestComponent {
|
||||
|
||||
ConsoleRegistryLockAction consoleRegistryLockAction();
|
||||
|
||||
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
|
||||
|
||||
ConsoleUiAction consoleUiAction();
|
||||
|
||||
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();
|
||||
|
||||
@@ -76,7 +76,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// they are global, and might have different roles for different domains.
|
||||
if (contact.isPresent() && isAuthorized(contact.get())) {
|
||||
return rdapJsonFormatter.createRdapContactEntity(
|
||||
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
|
||||
contact, ImmutableSet.of(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,7 +461,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
.entitySearchResultsBuilder()
|
||||
.add(
|
||||
rdapJsonFormatter.createRdapContactEntity(
|
||||
contact, ImmutableSet.of(), outputDataType));
|
||||
Optional.of(contact), ImmutableSet.of(), outputDataType));
|
||||
newCursor =
|
||||
Optional.of(
|
||||
CONTACT_CURSOR_PREFIX
|
||||
|
||||
@@ -61,6 +61,7 @@ import google.registry.rdap.RdapDataStructures.RdapStatus;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapContactEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapDomain;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity.Role;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.SecureDns;
|
||||
@@ -74,6 +75,7 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -249,6 +251,16 @@ public class RdapJsonFormatter {
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
Ordering.natural().onResultOf(DesignatedContact::getType);
|
||||
|
||||
/**
|
||||
* The list of RDAP contact roles that are required to be present on each domain.
|
||||
*
|
||||
* <p>Per RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and
|
||||
* MAY have others. We also have the BILLING role in our system but it isn't required and is only
|
||||
* listed if actually present.
|
||||
*/
|
||||
private static final ImmutableSet<Role> REQUIRED_CONTACT_ROLES =
|
||||
ImmutableSet.of(Role.REGISTRANT, Role.ADMIN, Role.TECH);
|
||||
|
||||
/** Creates the TOS notice that is added to every reply. */
|
||||
Notice createTosNotice() {
|
||||
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
|
||||
@@ -369,38 +381,53 @@ public class RdapJsonFormatter {
|
||||
() ->
|
||||
ImmutableSet.copyOf(replicaTm().loadByKeys(domain.getNameservers()).values()));
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
ImmutableSet<VKey<Contact>> contacts = domain.getReferencedContacts();
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> loadedContacts =
|
||||
replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(domain.getReferencedContacts()));
|
||||
// RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY
|
||||
// have others. We also add the BILLING.
|
||||
//
|
||||
contacts.isEmpty()
|
||||
? ImmutableMap.of()
|
||||
: replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(contacts));
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses redaction of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// the GDPR redaction is handled in createRdapContactEntity
|
||||
// The GDPR redaction is handled in createRdapContactEntity.
|
||||
|
||||
// Load all contacts that are present and group them by type (it is common for a single contact
|
||||
// entity to be used across multiple contact types on domain, e.g. registrant and admin).
|
||||
ImmutableSetMultimap<VKey<Contact>, Type> contactsToRoles =
|
||||
Streams.concat(
|
||||
domain.getContacts().stream(),
|
||||
Stream.of(DesignatedContact.create(Type.REGISTRANT, domain.getRegistrant())))
|
||||
domain.getAllContacts().stream()
|
||||
.sorted(DESIGNATED_CONTACT_ORDERING)
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getContactKey, DesignatedContact::getType));
|
||||
|
||||
// Convert the contact entities to RDAP output contacts (this also converts the contact types
|
||||
// to RDAP roles).
|
||||
Set<RdapContactEntity> rdapContacts = new LinkedHashSet<>();
|
||||
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
|
||||
Set<RdapEntity.Role> roles =
|
||||
Set<Role> roles =
|
||||
contactsToRoles.get(contactKey).stream()
|
||||
.map(RdapJsonFormatter::convertContactTypeToRdapRole)
|
||||
.collect(toImmutableSet());
|
||||
if (roles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
builder
|
||||
.entitiesBuilder()
|
||||
.add(
|
||||
createRdapContactEntity(
|
||||
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.ofNullable(loadedContacts.get(contactKey)), roles, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// Loop through all required contact roles and fill in placeholder REDACTED info for any
|
||||
// required ones that are missing, i.e. because of minimum registration data set.
|
||||
for (Role role : REQUIRED_CONTACT_ROLES) {
|
||||
if (rdapContacts.stream().noneMatch(c -> c.roles().contains(role))) {
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.empty(), ImmutableSet.of(role), OutputDataType.INTERNAL));
|
||||
}
|
||||
}
|
||||
builder.entitiesBuilder().addAll(rdapContacts);
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
@@ -502,25 +529,36 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a JSON object for a {@link Contact} and associated contact type.
|
||||
*
|
||||
* <p>If the contact isn't present (i.e. because of minimum registration data set), then always
|
||||
* show all of its fields as if they were redacted, and always deny RDAP authorization.
|
||||
*
|
||||
* @param contact the contact resource object from which the JSON object should be created
|
||||
* @param roles the roles of this contact
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapContactEntity createRdapContactEntity(
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
Optional<Contact> contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// TODO(mcilwain): Once the RDAP profile is fully updated for minimum registration data set,
|
||||
// we will want to not include non-existent contacts at all, rather than
|
||||
// pretending they exist and just showing REDACTED info. This is especially
|
||||
// important for authorized flows, where you wouldn't expect to see redaction
|
||||
// (although no one actually has access to authorized flows yet).
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
contact.isPresent()
|
||||
&& rdapAuthorization.isAuthorizedForRegistrar(
|
||||
contact.get().getCurrentSponsorRegistrarId());
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
fillRdapContactEntityWhenAuthorized(
|
||||
contactBuilder, vcardBuilder, contact.get(), outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -45,12 +44,11 @@ import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusValueType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsOrKeyType;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Utility class that turns {@link Domain} as {@link XjcRdeDomainElement}. */
|
||||
final class DomainToXjcConverter {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** Converts {@link Domain} to {@link XjcRdeDomainElement}. */
|
||||
static XjcRdeDomainElement convert(Domain domain, RdeMode mode) {
|
||||
return new XjcRdeDomainElement(convertDomain(domain, mode));
|
||||
@@ -168,11 +166,9 @@ final class DomainToXjcConverter {
|
||||
// o An OPTIONAL <registrant> element that contain the identifier for
|
||||
// the human or organizational social information object associated
|
||||
// as the holder of the domain name object.
|
||||
VKey<Contact> registrant = model.getRegistrant();
|
||||
if (registrant == null) {
|
||||
logger.atWarning().log("Domain %s has no registrant contact.", domainName);
|
||||
} else {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant));
|
||||
Optional<VKey<Contact>> registrant = model.getRegistrant();
|
||||
if (registrant.isPresent()) {
|
||||
Contact registrantContact = tm().transact(() -> tm().loadByKey(registrant.get()));
|
||||
checkState(
|
||||
registrantContact != null,
|
||||
"Registrant contact %s on domain %s does not exist",
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2024 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 com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureName;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.tools.params.TransitionListParameter.FeatureStatusTransitions;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command for creating and updating {@link FeatureFlag} objects. */
|
||||
@Parameters(separators = " =", commandDescription = "Create or update a feature flag.")
|
||||
public class ConfigureFeatureFlagCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(description = "Feature flag name(s) to create or update", required = true)
|
||||
private List<FeatureName> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = "--status_map",
|
||||
converter = FeatureStatusTransitions.class,
|
||||
validateWith = FeatureStatusTransitions.class,
|
||||
description =
|
||||
"Comma-delimited list of feature status transitions effective on specific dates, of the"
|
||||
+ " form <time>=<status>[,<time>=<status>]* where each status represents the status"
|
||||
+ " of the feature flag.",
|
||||
required = true)
|
||||
private ImmutableSortedMap<DateTime, FeatureStatus> featureStatusTransitions;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
for (FeatureName name : mainParameters) {
|
||||
Optional<FeatureFlag> oldFlag = FeatureFlag.getUncached(name);
|
||||
FeatureFlag.Builder newFlagBuilder =
|
||||
new FeatureFlag().asBuilder().setFeatureName(name).setStatusMap(featureStatusTransitions);
|
||||
stageEntityChange(oldFlag.orElse(null), newFlagBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -111,8 +111,8 @@ abstract class CreateOrUpdateBulkPricingPackageCommand extends MutatingCommand {
|
||||
if (clearLastNotificationSent()) {
|
||||
builder.setLastNotificationSent(null);
|
||||
}
|
||||
BulkPricingPackage newBUlkPricingPackage = builder.build();
|
||||
stageEntityChange(oldBulkPricingPackage, newBUlkPricingPackage);
|
||||
BulkPricingPackage newBulkPricingPackage = builder.build();
|
||||
stageEntityChange(oldBulkPricingPackage, newBulkPricingPackage);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,30 +15,25 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.console.User.grantIapPermission;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to create a new User. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand implements CommandWithConnection {
|
||||
|
||||
static final String IAP_SECURED_WEB_APP_USER_ROLE = "roles/iap.httpsResourceAccessor";
|
||||
static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private ServiceConnection connection;
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
@@ -53,29 +48,7 @@ public class CreateUserCommand extends CreateOrUpdateUserCommand implements Comm
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
String ret = super.execute();
|
||||
String groupEmailAddress = maybeGroupEmailAddress.orElse(null);
|
||||
if (groupEmailAddress != null) {
|
||||
logger.atInfo().log("Adding %s to group %s", email, groupEmailAddress);
|
||||
connection.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
email,
|
||||
"groupEmailAddress",
|
||||
groupEmailAddress,
|
||||
"groupUpdateMode",
|
||||
"ADD"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
} else {
|
||||
logger.atInfo().log("Granting IAP role to user %s", email);
|
||||
iamClient.addBinding(email, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
}
|
||||
grantIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(ServiceConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,32 +15,27 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.tools.CreateUserCommand.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Deletes a {@link User}. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete a user account")
|
||||
public class DeleteUserCommand extends ConfirmingCommand implements CommandWithConnection {
|
||||
public class DeleteUserCommand extends ConfirmingCommand {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private ServiceConnection connection;
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
@@ -49,11 +44,6 @@ public class DeleteUserCommand extends ConfirmingCommand implements CommandWithC
|
||||
@Parameter(names = "--email", description = "Email address of the user", required = true)
|
||||
String email;
|
||||
|
||||
@Override
|
||||
public void setConnection(ServiceConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
@@ -69,24 +59,7 @@ public class DeleteUserCommand extends ConfirmingCommand implements CommandWithC
|
||||
checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user");
|
||||
tm().delete(optionalUser.get());
|
||||
});
|
||||
String groupEmailAddress = maybeGroupEmailAddress.orElse(null);
|
||||
if (groupEmailAddress != null) {
|
||||
logger.atInfo().log("Removing %s from group %s", email, groupEmailAddress);
|
||||
connection.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
email,
|
||||
"groupEmailAddress",
|
||||
groupEmailAddress,
|
||||
"groupUpdateMode",
|
||||
"REMOVE"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
} else {
|
||||
logger.atInfo().log("Removing IAP role from user %s", email);
|
||||
iamClient.removeBinding(email, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
}
|
||||
User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
return String.format("Deleted user with email %s", email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.internal.Nullable;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -62,6 +63,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to generate and persist {@link AllocationToken}s. */
|
||||
@@ -158,10 +160,16 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
"The type of renewal price behavior, either DEFAULT (default), NONPREMIUM, or SPECIFIED."
|
||||
+ " This indicates how a domain should be charged for renewal. By default, a domain"
|
||||
+ " will be renewed at the renewal price from the pricing engine. If the renewal"
|
||||
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be the"
|
||||
+ " same as the domain's calculated create price.")
|
||||
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be equal"
|
||||
+ " to the provided renewal price.")
|
||||
private RenewalPriceBehavior renewalPriceBehavior = DEFAULT;
|
||||
|
||||
@Parameter(
|
||||
names = {"--renewal_price"},
|
||||
description = "The renewal price amount iff the renewal price behavior is SPECIFIED.")
|
||||
@Nullable
|
||||
private Money renewalPrice;
|
||||
|
||||
@Parameter(
|
||||
names = {"--registration_behavior"},
|
||||
description =
|
||||
@@ -228,6 +236,7 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
Optional.ofNullable(discountYears).ifPresent(token::setDiscountYears);
|
||||
Optional.ofNullable(tokenStatusTransitions)
|
||||
.ifPresent(token::setTokenStatusTransitions);
|
||||
Optional.ofNullable(renewalPrice).ifPresent(token::setRenewalPrice);
|
||||
Optional.ofNullable(domainNames)
|
||||
.ifPresent(d -> token.setDomainName(d.removeFirst()));
|
||||
return token.build();
|
||||
@@ -295,6 +304,10 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
+ " map");
|
||||
}
|
||||
|
||||
checkArgument(
|
||||
renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED) == (renewalPrice != null),
|
||||
"renewal_price must be specified iff renewal_price_behavior is SPECIFIED");
|
||||
|
||||
if (tokenStrings != null) {
|
||||
verifyTokenStringsDoNotExist();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024 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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureFlagNotFoundException;
|
||||
import google.registry.model.common.FeatureFlag.FeatureName;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to show a {@link FeatureFlag}. */
|
||||
@Parameters(separators = " =", commandDescription = "Show FeatureFlag record(s)")
|
||||
public class GetFeatureFlagCommand implements Command {
|
||||
|
||||
@Parameter(description = "Feature flag(s) to show", required = true)
|
||||
private List<FeatureName> mainParameters;
|
||||
|
||||
@Inject ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// Don't use try-with-resources to manage standard output streams, closing the stream will
|
||||
// cause subsequent output to standard output or standard error to be lost
|
||||
// See: https://errorprone.info/bugpattern/ClosingStandardOutputStreams
|
||||
PrintStream printStream = new PrintStream(System.out, false, UTF_8);
|
||||
for (FeatureName featureFlag : mainParameters) {
|
||||
Optional<FeatureFlag> maybeFeatureFlag = FeatureFlag.getUncached(featureFlag);
|
||||
if (maybeFeatureFlag.isEmpty()) {
|
||||
throw new FeatureFlagNotFoundException(featureFlag);
|
||||
}
|
||||
printStream.println(objectMapper.writeValueAsString(maybeFeatureFlag.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import com.google.api.services.cloudresourcemanager.model.GetIamPolicyRequest;
|
||||
import com.google.api.services.cloudresourcemanager.model.Policy;
|
||||
import com.google.api.services.cloudresourcemanager.model.SetIamPolicyRequest;
|
||||
import com.google.common.base.Ascii;
|
||||
import google.registry.config.CredentialModule.LocalCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.IOException;
|
||||
@@ -38,7 +38,7 @@ public class IamClient {
|
||||
|
||||
@Inject
|
||||
public IamClient(
|
||||
@LocalCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
this(
|
||||
new CloudResourceManager.Builder(
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2024 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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import java.io.PrintStream;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to list all {@link google.registry.model.common.FeatureFlag} objects. */
|
||||
@Parameters(separators = " =", commandDescription = "List all feature flags.")
|
||||
public class ListFeatureFlagsCommand implements Command {
|
||||
|
||||
@Inject ObjectMapper mapper;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// Don't use try-with-resources to manage standard output streams, closing the stream will
|
||||
// cause subsequent output to standard output or standard error to be lost
|
||||
// See: https://errorprone.info/bugpattern/ClosingStandardOutputStreams
|
||||
PrintStream printStream = new PrintStream(System.out, false, UTF_8);
|
||||
ImmutableList<FeatureFlag> featureFlags = FeatureFlag.getAllUncached();
|
||||
for (FeatureFlag featureFlag : featureFlags) {
|
||||
printStream.println(mapper.writeValueAsString(featureFlag));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ public final class RegistryTool {
|
||||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("check_domain", CheckDomainCommand.class)
|
||||
.put("check_domain_claims", CheckDomainClaimsCommand.class)
|
||||
.put("configure_feature_flag", ConfigureFeatureFlagCommand.class)
|
||||
.put("configure_tld", ConfigureTldCommand.class)
|
||||
.put("convert_idn", ConvertIdnCommand.class)
|
||||
.put("count_domains", CountDomainsCommand.class)
|
||||
@@ -71,6 +72,7 @@ public final class RegistryTool {
|
||||
.put("get_claims_list", GetClaimsListCommand.class)
|
||||
.put("get_contact", GetContactCommand.class)
|
||||
.put("get_domain", GetDomainCommand.class)
|
||||
.put("get_feature_flag", GetFeatureFlagCommand.class)
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
.put("get_host", GetHostCommand.class)
|
||||
.put("get_keyring_secret", GetKeyringSecretCommand.class)
|
||||
@@ -85,6 +87,7 @@ public final class RegistryTool {
|
||||
.put("hash_certificate", HashCertificateCommand.class)
|
||||
.put("list_cursors", ListCursorsCommand.class)
|
||||
.put("list_domains", ListDomainsCommand.class)
|
||||
.put("list_feature_flags", ListFeatureFlagsCommand.class)
|
||||
.put("list_hosts", ListHostsCommand.class)
|
||||
.put("list_premium_lists", ListPremiumListsCommand.class)
|
||||
.put("list_registrars", ListRegistrarsCommand.class)
|
||||
|
||||
@@ -119,6 +119,8 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(GetDomainCommand command);
|
||||
|
||||
void inject(GetFeatureFlagCommand command);
|
||||
|
||||
void inject(GetHostCommand command);
|
||||
|
||||
void inject(GetKeyringSecretCommand command);
|
||||
@@ -131,6 +133,8 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(ListCursorsCommand command);
|
||||
|
||||
void inject(ListFeatureFlagsCommand command);
|
||||
|
||||
void inject(LockDomainCommand command);
|
||||
|
||||
void inject(LoginCommand command);
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.tools;
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.LocalCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
|
||||
@@ -27,7 +27,7 @@ public class RegistryToolDataflowModule {
|
||||
|
||||
@Provides
|
||||
static Dataflow provideDataflow(
|
||||
@LocalCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Dataflow.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.MoreFiles;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.OteAccountBuilder;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import google.registry.util.Clock;
|
||||
@@ -30,6 +32,7 @@ import google.registry.util.StringGenerator;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
@@ -47,7 +50,7 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--ip_allow_list"},
|
||||
description = "Comma-separated list of IP addreses or CIDR ranges.",
|
||||
description = "Comma-separated list of IP addresses or CIDR ranges.",
|
||||
required = true)
|
||||
private List<String> ipAllowList = new ArrayList<>();
|
||||
|
||||
@@ -55,7 +58,7 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
names = {"--email"},
|
||||
description =
|
||||
"The registrar's account to use for console access. "
|
||||
+ "Must be on the registry's G Suite domain.",
|
||||
+ "Must be on the registry's Google Workspace domain.",
|
||||
required = true)
|
||||
private String email;
|
||||
|
||||
@@ -76,6 +79,14 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
|
||||
OteAccountBuilder oteAccountBuilder;
|
||||
String password;
|
||||
|
||||
@@ -87,7 +98,7 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
password = passwordGenerator.createString(PASSWORD_LENGTH);
|
||||
oteAccountBuilder =
|
||||
OteAccountBuilder.forRegistrarId(registrar)
|
||||
.addContact(email)
|
||||
.addUser(email)
|
||||
.setPassword(password)
|
||||
.setIpAllowList(ipAllowList)
|
||||
.setReplaceExisting(overwrite);
|
||||
@@ -114,8 +125,11 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
&& RegistryEnvironment.get() != RegistryEnvironment.UNITTEST) {
|
||||
builder.append(
|
||||
String.format(
|
||||
"\n\nWARNING: Running against %s environment. Are "
|
||||
+ "you sure you didn\'t mean to run this against sandbox (e.g. \"-e SANDBOX\")?",
|
||||
"""
|
||||
|
||||
|
||||
WARNING: Running against %s environment. Are \
|
||||
you sure you didn't mean to run this against sandbox (e.g. "-e SANDBOX")?""",
|
||||
RegistryEnvironment.get()));
|
||||
}
|
||||
|
||||
@@ -125,15 +139,15 @@ final class SetupOteCommand extends ConfirmingCommand {
|
||||
@Override
|
||||
public String execute() {
|
||||
ImmutableMap<String, String> clientIdToTld = oteAccountBuilder.buildAndPersist();
|
||||
oteAccountBuilder.grantIapPermission(maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
output.append("Copy these usernames/passwords back into the onboarding bug:\n\n");
|
||||
clientIdToTld.forEach(
|
||||
(clientId, tld) -> {
|
||||
output.append(
|
||||
String.format("Login: %s\nPassword: %s\nTLD: %s\n\n", clientId, password, tld));
|
||||
});
|
||||
(clientId, tld) ->
|
||||
output.append(
|
||||
String.format("Login: %s\nPassword: %s\nTLD: %s\n\n", clientId, password, tld)));
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to update existing {@link AllocationToken}s. */
|
||||
@@ -110,11 +111,17 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
"The type of renewal price behavior, either DEFAULT (default), NONPREMIUM, or SPECIFIED."
|
||||
+ " This indicates how a domain should be charged for renewal. By default, a domain"
|
||||
+ " will be renewed at the renewal price from the pricing engine. If the renewal"
|
||||
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be the"
|
||||
+ " same as the domain's calculated create price.")
|
||||
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be equal"
|
||||
+ " to the provided renewal price.")
|
||||
@Nullable
|
||||
private RenewalPriceBehavior renewalPriceBehavior;
|
||||
|
||||
@Parameter(
|
||||
names = {"--renewal_price"},
|
||||
description = "The renewal price amount iff the renewal price behavior is SPECIFIED.")
|
||||
@Nullable
|
||||
private Money renewalPrice;
|
||||
|
||||
@Parameter(
|
||||
names = {"--registration_behavior"},
|
||||
description =
|
||||
@@ -189,17 +196,22 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
.ifPresent(tlds -> builder.setAllowedTlds(ImmutableSet.copyOf(tlds)));
|
||||
Optional.ofNullable(allowedEppActions)
|
||||
.ifPresent(
|
||||
eppActions -> {
|
||||
builder.setAllowedEppActions(
|
||||
eppActions.stream()
|
||||
.map(CommandName::parseKnownCommand)
|
||||
.collect(toImmutableSet()));
|
||||
});
|
||||
eppActions ->
|
||||
builder.setAllowedEppActions(
|
||||
eppActions.stream()
|
||||
.map(CommandName::parseKnownCommand)
|
||||
.collect(toImmutableSet())));
|
||||
Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction);
|
||||
Optional.ofNullable(discountPremiums).ifPresent(builder::setDiscountPremiums);
|
||||
Optional.ofNullable(discountYears).ifPresent(builder::setDiscountYears);
|
||||
Optional.ofNullable(tokenStatusTransitions).ifPresent(builder::setTokenStatusTransitions);
|
||||
|
||||
if (renewalPriceBehavior != null
|
||||
&& renewalPriceBehavior != original.getRenewalPriceBehavior()) {
|
||||
builder.setRenewalPrice(null);
|
||||
}
|
||||
Optional.ofNullable(renewalPriceBehavior).ifPresent(builder::setRenewalPriceBehavior);
|
||||
Optional.ofNullable(renewalPrice).ifPresent(builder::setRenewalPrice);
|
||||
Optional.ofNullable(registrationBehavior).ifPresent(builder::setRegistrationBehavior);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import org.joda.money.Money;
|
||||
@@ -72,4 +73,12 @@ public abstract class TransitionListParameter<V> extends KeyValueMapParameter<Da
|
||||
return TokenStatus.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Converter-validator for feature status transitions. */
|
||||
public static class FeatureStatusTransitions extends TransitionListParameter<FeatureStatus> {
|
||||
@Override
|
||||
protected FeatureStatus parseValue(String value) {
|
||||
return FeatureStatus.valueOf(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import javax.inject.Inject;
|
||||
public class UpdateUserGroupAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/admin/updateUserGroup";
|
||||
public static final String GROUP_UPDATE_QUEUE = "console-user-group-update";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -53,7 +54,7 @@ public class UpdateUserGroupAction implements Runnable {
|
||||
@Inject
|
||||
UpdateUserGroupAction() {}
|
||||
|
||||
enum Mode {
|
||||
public enum Mode {
|
||||
ADD,
|
||||
REMOVE
|
||||
}
|
||||
|
||||
@@ -254,5 +254,4 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-11
@@ -47,10 +47,8 @@ import google.registry.util.EmailMessage;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
@@ -153,14 +151,9 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
private void sendVerificationEmail(RegistryLock lock, String userEmail, boolean isLock) {
|
||||
try {
|
||||
String url =
|
||||
new URIBuilder()
|
||||
.setScheme("https")
|
||||
.setHost(consoleApiParams.request().getServerName())
|
||||
// TODO: replace this with the PATH in ConsoleRegistryLockVerifyAction once it exists
|
||||
.setPath("/console-api/registry-lock-verify")
|
||||
.setParameter("lockVerificationCode", lock.getVerificationCode())
|
||||
.build()
|
||||
.toString();
|
||||
String.format(
|
||||
"https://%s/console/#/registry-lock-verify?lockVerificationCode=%s",
|
||||
consoleApiParams.request().getServerName(), lock.getVerificationCode());
|
||||
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, lock.getDomainName(), url);
|
||||
ImmutableList<InternetAddress> recipients =
|
||||
ImmutableList.of(new InternetAddress(userEmail, true));
|
||||
@@ -171,7 +164,7 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
.setSubject(String.format("Registry %s verification", action))
|
||||
.setRecipients(recipients)
|
||||
.build());
|
||||
} catch (AddressException | URISyntaxException e) {
|
||||
} catch (AddressException e) {
|
||||
throw new RuntimeException(e); // caught above -- this is so we can run in a transaction
|
||||
}
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Copyright 2024 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.ui.server.console;
|
||||
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Handler for verifying registry lock requests, a form of 2FA. */
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleRegistryLockVerifyAction.PATH,
|
||||
method = {GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/registry-lock-verify";
|
||||
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final Gson gson;
|
||||
private final String lockVerificationCode;
|
||||
|
||||
@Inject
|
||||
public ConsoleRegistryLockVerifyAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
DomainLockUtils domainLockUtils,
|
||||
Gson gson,
|
||||
@Parameter("lockVerificationCode") String lockVerificationCode) {
|
||||
super(consoleApiParams);
|
||||
this.domainLockUtils = domainLockUtils;
|
||||
this.gson = gson;
|
||||
this.lockVerificationCode = lockVerificationCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.verifyVerificationCode(lockVerificationCode, user.getUserRoles().isAdmin());
|
||||
RegistryLockAction action =
|
||||
lock.getLockCompletionTime().isPresent()
|
||||
? RegistryLockAction.UNLOCKED
|
||||
: RegistryLockAction.LOCKED;
|
||||
RegistryLockVerificationResponse lockResponse =
|
||||
new RegistryLockVerificationResponse(
|
||||
Ascii.toLowerCase(action.toString()), lock.getDomainName(), lock.getRegistrarId());
|
||||
consoleApiParams.response().setPayload(gson.toJson(lockResponse));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private enum RegistryLockAction {
|
||||
LOCKED,
|
||||
UNLOCKED
|
||||
}
|
||||
|
||||
private record RegistryLockVerificationResponse(
|
||||
@Expose String action, @Expose String domainName, @Expose String registrarId) {}
|
||||
}
|
||||
+1
-3
@@ -47,8 +47,7 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
@Inject
|
||||
ConsoleUpdateRegistrarAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrar") Optional<Registrar> registrar) {
|
||||
ConsoleApiParams consoleApiParams, @Parameter("registrar") Optional<Registrar> registrar) {
|
||||
super(consoleApiParams);
|
||||
this.registrar = registrar;
|
||||
}
|
||||
@@ -108,5 +107,4 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.OteAccountBuilder;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Method;
|
||||
@@ -31,6 +33,7 @@ import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.OteSetupConsoleSoyInfo;
|
||||
@@ -87,6 +90,14 @@ public final class ConsoleOteSetupAction extends HtmlAction {
|
||||
@Parameter("password")
|
||||
Optional<String> optionalPassword;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
|
||||
@Inject
|
||||
ConsoleOteSetupAction() {}
|
||||
|
||||
@@ -107,16 +118,11 @@ public final class ConsoleOteSetupAction extends HtmlAction {
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case POST -> {
|
||||
runPost(data);
|
||||
}
|
||||
case GET -> {
|
||||
runGet(data);
|
||||
}
|
||||
default -> {
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
case POST -> runPost(data);
|
||||
case GET -> runGet(data);
|
||||
default ->
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,11 +139,13 @@ public final class ConsoleOteSetupAction extends HtmlAction {
|
||||
data.put("contactEmail", email.get());
|
||||
|
||||
String password = optionalPassword.orElse(passwordGenerator.createString(PASSWORD_LENGTH));
|
||||
ImmutableMap<String, String> clientIdToTld =
|
||||
OteAccountBuilder oteAccountBuilder =
|
||||
OteAccountBuilder.forRegistrarId(clientId.get())
|
||||
.addContact(email.get())
|
||||
.setPassword(password)
|
||||
.buildAndPersist();
|
||||
.addUser(email.get())
|
||||
.setPassword(password);
|
||||
ImmutableMap<String, String> clientIdToTld = oteAccountBuilder.buildAndPersist();
|
||||
|
||||
oteAccountBuilder.grantIapPermission(maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
|
||||
sendExternalUpdates(clientIdToTld);
|
||||
|
||||
|
||||
+34
-18
@@ -26,10 +26,15 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarBase.State;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.Action.Service;
|
||||
@@ -37,6 +42,7 @@ import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.AnalyticsSoyInfo;
|
||||
@@ -95,6 +101,14 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
@Parameter("consoleName")
|
||||
Optional<String> name;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
|
||||
@Inject @Parameter("billingAccount") Optional<String> billingAccount;
|
||||
@Inject @Parameter("ianaId") Optional<Integer> ianaId;
|
||||
@Inject @Parameter("referralEmail") Optional<String> referralEmail;
|
||||
@@ -129,16 +143,11 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case POST -> {
|
||||
runPost(data);
|
||||
}
|
||||
case GET -> {
|
||||
runGet(data);
|
||||
}
|
||||
default -> {
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
case POST -> runPost(data);
|
||||
case GET -> runGet(data);
|
||||
default ->
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +178,8 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
list))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
list -> CurrencyUnit.of(Ascii.toUpperCase(list.get(0))), list -> list.get(1)));
|
||||
list -> CurrencyUnit.of(Ascii.toUpperCase(list.getFirst())),
|
||||
list -> list.get(1)));
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error parsing billing accounts - " + e.getMessage(), e);
|
||||
}
|
||||
@@ -233,12 +243,15 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
.setZip(optionalZip.orElse(null))
|
||||
.build())
|
||||
.build();
|
||||
RegistrarPoc contact =
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName(consoleUserEmail.get())
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(consoleUserEmail.get())
|
||||
.setLoginEmailAddress(consoleUserEmail.get())
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of(
|
||||
registrar.getRegistrarId(), RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build();
|
||||
tm().transact(
|
||||
() -> {
|
||||
@@ -246,8 +259,11 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
Registrar.loadByRegistrarId(registrar.getRegistrarId()).isEmpty(),
|
||||
"Registrar with client ID %s already exists",
|
||||
registrar.getRegistrarId());
|
||||
tm().putAll(registrar, contact);
|
||||
tm().put(registrar);
|
||||
});
|
||||
UserDao.saveUser(user);
|
||||
User.grantIapPermission(
|
||||
user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
data.put("password", password);
|
||||
data.put("passcode", phonePasscode);
|
||||
|
||||
|
||||
+1
-25
@@ -88,22 +88,6 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
static final String ARGS_PARAM = "args";
|
||||
static final String ID_PARAM = "id";
|
||||
|
||||
/**
|
||||
* Allows task enqueueing to be disabled when executing registrar console test cases.
|
||||
*
|
||||
* <p>The existing workflow in UI test cases triggers task enqueueing, which was not an issue with
|
||||
* Task Queue since it's a native App Engine feature simulated by the App Engine SDK's
|
||||
* environment. However, with Cloud Tasks, the server enqueues and fails to deliver to the actual
|
||||
* Cloud Tasks endpoint due to lack of permission.
|
||||
*
|
||||
* <p>One way to allow enqueuing in backend test and avoid enqueuing in UI test is to disable
|
||||
* enqueuing when the test server starts and enable enqueueing once the test server stops. This
|
||||
* can be done by utilizing a ThreadLocal<Boolean> variable isInTestDriver, which is set to false
|
||||
* by default. Enqueuing is allowed only if the value of isInTestDriver is false. It's set to true
|
||||
* in start() and set to false in stop() inside TestDriver.java, a class used in testing.
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> isInTestDriver = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject RegistrarConsoleMetrics registrarConsoleMetrics;
|
||||
@Inject SendEmailUtils sendEmailUtils;
|
||||
@@ -118,14 +102,6 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
return contact.getPhoneNumber() != null;
|
||||
}
|
||||
|
||||
public static void setIsInTestDriverToFalse() {
|
||||
isInTestDriver.set(false);
|
||||
}
|
||||
|
||||
public static void setIsInTestDriverToTrue() {
|
||||
isInTestDriver.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
@@ -623,7 +599,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
if (CollectionUtils.difference(changedKeys, "lastUpdateTime").isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!isInTestDriver.get()) {
|
||||
if (!RegistryEnvironment.isInTestServer()) {
|
||||
// Enqueues a sync registrar sheet task if enqueuing is not triggered by console tests and
|
||||
// there's an update besides the lastUpdateTime
|
||||
cloudTasksUtils.enqueue(
|
||||
|
||||
@@ -105,7 +105,9 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
"Registrar Abuse Contact Phone",
|
||||
abuseContact.map(RegistrarPoc::getPhoneNumber).orElse(""))
|
||||
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
|
||||
.emitContact("Registrant", Optional.of(domain.getRegistrant()), preferUnicode)
|
||||
// TODO(mcilwain): Investigate if the WHOIS spec requires us to always output REDACTED
|
||||
// text in WHOIS even if the contact is not present, and if so, do so.
|
||||
.emitContact("Registrant", domain.getRegistrant(), preferUnicode)
|
||||
.emitContact("Admin", getContactReference(Type.ADMIN), preferUnicode)
|
||||
.emitContact("Tech", getContactReference(Type.TECH), preferUnicode)
|
||||
.emitContact("Billing", getContactReference(Type.BILLING), preferUnicode)
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistEppResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -111,8 +112,9 @@ public class CheckBulkComplianceActionTest {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -206,8 +208,9 @@ public class CheckBulkComplianceActionTest {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -263,8 +266,9 @@ public class CheckBulkComplianceActionTest {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage packagePromotion2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -338,8 +342,9 @@ public class CheckBulkComplianceActionTest {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -404,8 +409,9 @@ public class CheckBulkComplianceActionTest {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.testing.PAssert;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
@@ -191,7 +192,7 @@ public class RegistryJpaReadTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
|
||||
@@ -31,6 +31,7 @@ import static google.registry.rde.RdeResourceType.HOST;
|
||||
import static google.registry.rde.RdeResourceType.REGISTRAR;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
@@ -83,10 +84,10 @@ import google.registry.rde.PendingDeposit;
|
||||
import google.registry.rde.RdeResourceType;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeKeyringModule;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -266,23 +267,23 @@ public class RdePipelineTest {
|
||||
persistHostHistory(host1);
|
||||
Domain helloDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("hello.soy", contact1)
|
||||
.asBuilder()
|
||||
.addNameserver(host1.createVKey())
|
||||
.build());
|
||||
newDomain("hello.soy", contact1).asBuilder().addNameserver(host1.createVKey()).build());
|
||||
persistDomainHistory(helloDomain);
|
||||
persistHostHistory(persistActiveHost("not-used-subordinate.hello.soy"));
|
||||
Host host2 = persistActiveHost("ns1.hello.soy");
|
||||
persistHostHistory(host2);
|
||||
|
||||
// This domain has no registrant.
|
||||
Domain kittyDomain =
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("kitty.fun", contact2)
|
||||
newDomain("kitty.fun", contact2)
|
||||
.asBuilder()
|
||||
.addNameservers(ImmutableSet.of(host1.createVKey(), host2.createVKey()))
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
persistDomainHistory(kittyDomain);
|
||||
// Should not appear because the TLD is not included in a pending deposit.
|
||||
persistDomainHistory(persistEppResource(DatabaseHelper.newDomain("lol.cat", contact1)));
|
||||
persistDomainHistory(persistEppResource(newDomain("lol.cat", contact1)));
|
||||
// To be deleted.
|
||||
Domain deletedDomain = persistActiveDomain("deleted.soy");
|
||||
persistDomainHistory(deletedDomain);
|
||||
@@ -325,7 +326,7 @@ public class RdePipelineTest {
|
||||
persistHostHistory(futureHost);
|
||||
persistDomainHistory(
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("future.soy", futureContact)
|
||||
newDomain("future.soy", futureContact)
|
||||
.asBuilder()
|
||||
.setNameservers(futureHost.createVKey())
|
||||
.build()));
|
||||
@@ -379,18 +380,30 @@ public class RdePipelineTest {
|
||||
// The same registrars are attached to all the pending deposits.
|
||||
.containsExactly("New Registrar", "The Registrar", "external_monitoring");
|
||||
// Domain fragments.
|
||||
if ("soy".equals(kv.getKey().tld())) {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("hello.soy");
|
||||
} else {
|
||||
assertThat(
|
||||
getFragmentForType(kv, DOMAIN)
|
||||
.map(getXmlElement(DOMAIN_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("cat.fun");
|
||||
ImmutableSet<DepositFragment> domainFrags =
|
||||
getFragmentForType(kv, DOMAIN).collect(toImmutableSet());
|
||||
assertThat(domainFrags).hasSize(1);
|
||||
if ("fun".equals(kv.getKey().tld())) {
|
||||
// Note that this fragment contains no registrant (which is valid).
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>cat.fun</rdeDomain:name>
|
||||
<rdeDomain:roid>15-FUN</rdeDomain:roid>
|
||||
<rdeDomain:uName>cat.fun</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:contact type="admin">contact456</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact456</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.hello.soy</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
if (kv.getKey().mode().equals(FULL)) {
|
||||
// Contact fragments for hello.soy.
|
||||
@@ -400,12 +413,35 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contact1234", "contact789");
|
||||
|
||||
// Host fragments for hello.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
.map(getXmlElement(HOST_NAME_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("ns1.external.tld", "ns1.lol.cat");
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains a registrant.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:registrant>contact1234</rdeDomain:registrant>
|
||||
<rdeDomain:contact type="admin">contact789</rdeDomain:contact>
|
||||
<rdeDomain:contact type="tech">contact1234</rdeDomain:contact>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
} else {
|
||||
// Contact fragments for cat.fun.
|
||||
assertThat(
|
||||
@@ -413,6 +449,7 @@ public class RdePipelineTest {
|
||||
.map(getXmlElement(CONTACT_ID_PATTERN))
|
||||
.collect(toImmutableSet()))
|
||||
.containsExactly("contactABC");
|
||||
|
||||
// Host fragments for cat.soy.
|
||||
assertThat(
|
||||
getFragmentForType(kv, HOST)
|
||||
@@ -429,6 +466,25 @@ public class RdePipelineTest {
|
||||
fragment.type().equals(CONTACT)
|
||||
|| fragment.type().equals(HOST)))
|
||||
.isFalse();
|
||||
|
||||
// Domain fragments for hello.soy: Note that this contains no contact info.
|
||||
assertThat(domainFrags.stream().findFirst().get().xml().strip())
|
||||
.isEqualTo(
|
||||
"""
|
||||
<rdeDomain:domain>
|
||||
<rdeDomain:name>hello.soy</rdeDomain:name>
|
||||
<rdeDomain:roid>E-SOY</rdeDomain:roid>
|
||||
<rdeDomain:uName>hello.soy</rdeDomain:uName>
|
||||
<rdeDomain:status s="ok"/>
|
||||
<rdeDomain:ns>
|
||||
<domain:hostObj>ns1.external.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.lol.cat</domain:hostObj>
|
||||
</rdeDomain:ns>
|
||||
<rdeDomain:clID>TheRegistrar</rdeDomain:clID>
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
|
||||
@@ -54,6 +54,7 @@ import google.registry.util.Retrier;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.SerializableCoder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
@@ -297,7 +298,7 @@ class Spec11PipelineTest {
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateRegistrarId(registrar.getRegistrarId())
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setPersistedCurrentSponsorRegistrarId(registrar.getRegistrarId())
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
|
||||
@@ -1371,6 +1371,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("resdom.tld")
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.build());
|
||||
// Despite the domain being FULLY_BLOCKED, the non-superuser create succeeds the domain is also
|
||||
// RESERVED_FOR_SPECIFIC_USE and the correct allocation token is passed.
|
||||
@@ -3413,6 +3414,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.build());
|
||||
assertThat(
|
||||
DomainCreateFlow.getRenewalPriceInfo(
|
||||
@@ -3439,6 +3441,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.build())),
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -3519,6 +3522,22 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(), token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonpremiumCreateToken() throws Exception {
|
||||
createTld("example");
|
||||
persistContactsAndHosts();
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRegistrationBehavior(RegistrationBehavior.NONPREMIUM_CREATE)
|
||||
.setDomainName("rich.example")
|
||||
.build());
|
||||
setEppInput(
|
||||
"domain_create_premium_allocationtoken.xml", ImmutableMap.of("YEARS", "1", "FEE", "13.00"));
|
||||
runFlowAssertResponse(loadFile("domain_create_nonpremium_token_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_quietPeriod_defaultTokenPresent() throws Exception {
|
||||
persistResource(
|
||||
@@ -3875,10 +3894,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.build());
|
||||
persistContactsAndHosts();
|
||||
setEppInput(
|
||||
@@ -3903,10 +3924,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.build());
|
||||
persistContactsAndHosts();
|
||||
setEppInput(
|
||||
|
||||
@@ -103,6 +103,7 @@ import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
@@ -162,7 +163,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(TIME_BEFORE_FLOW)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.build());
|
||||
earlierHistoryEntry =
|
||||
@@ -738,7 +739,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
DatabaseHelper.newDomain("example1.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey())
|
||||
Optional.of(
|
||||
loadByForeignKey(Contact.class, "sh8013", clock.nowUtc()).get().createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.setDeletionTime(START_OF_TIME)
|
||||
.build());
|
||||
|
||||
@@ -78,6 +78,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
@@ -139,7 +140,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
|
||||
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
|
||||
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
|
||||
.setRegistrant(registrant.createVKey())
|
||||
.setRegistrant(Optional.of(registrant.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
@@ -227,6 +228,26 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
doSuccessfulTest("domain_info_response.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noRegistrant() throws Exception {
|
||||
persistTestEntities(false);
|
||||
domain = persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
doSuccessfulTest("domain_info_response_no_registrant.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noContacts() throws Exception {
|
||||
persistTestEntities(false);
|
||||
domain =
|
||||
persistResource(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
doSuccessfulTest("domain_info_response_no_contacts.xml", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_clTridNotSpecified() throws Exception {
|
||||
setEppInput("domain_info_no_cltrid.xml");
|
||||
@@ -1097,8 +1118,9 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
@@ -1118,8 +1140,9 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
@@ -1151,8 +1174,9 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
|
||||
@@ -55,6 +55,7 @@ import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeHttpSession;
|
||||
import google.registry.util.Clock;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -146,7 +147,7 @@ public class DomainPricingLogicTest {
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
// (13 + 11) * 0.85 == 20.40
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 20.4).getAmount(), CREATE, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("20.40"), CREATE, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -172,7 +173,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("50.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -185,7 +186,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("100.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -198,7 +199,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 500).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("500.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -215,7 +216,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("100.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -241,7 +242,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("50.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -281,7 +282,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 500).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("500.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -308,7 +309,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 400).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("400.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -350,7 +351,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -376,7 +377,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("5.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -394,7 +395,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("50.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -421,7 +422,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("40.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -439,7 +440,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -466,7 +467,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("5.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -484,7 +485,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("50.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -512,7 +513,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("40.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -530,7 +531,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -548,7 +549,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("50.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -567,7 +568,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("1.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -597,7 +598,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("1.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -628,7 +629,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("1.00"), RENEW, false))
|
||||
.build());
|
||||
assertThat(
|
||||
Iterables.getLast(DatabaseHelper.loadAllOf(BillingRecurrence.class))
|
||||
@@ -651,7 +652,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("5.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -679,7 +680,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("5.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -698,7 +699,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 17).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("17.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -717,7 +718,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 85).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("85.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -739,7 +740,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -750,7 +751,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("100.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -765,7 +766,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -780,7 +781,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("100.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -796,7 +797,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -812,7 +813,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("10.00"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -829,7 +830,7 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1.23).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("1.23"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -846,7 +847,61 @@ public class DomainPricingLogicTest {
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1.23).getAmount(), RENEW, false))
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("1.23"), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainCreatePrice_nonPremiumCreate_unaffectedRenewal() throws EppException {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setDomainName("premium.example")
|
||||
.setRegistrationBehavior(AllocationToken.RegistrationBehavior.NONPREMIUM_CREATE)
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("13.00"), CREATE, false))
|
||||
.build());
|
||||
// Two-year create should be 13 (standard price) + 100 (premium price)
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("113.00"), CREATE, false))
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty()),
|
||||
Optional.of(allocationToken)))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(new BigDecimal("100.00"), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,9 +1257,11 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
persistDomain();
|
||||
@@ -1290,9 +1292,11 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
persistDomain();
|
||||
@@ -1327,9 +1331,11 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
@@ -1357,9 +1363,11 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
|
||||
@@ -388,7 +388,9 @@ class DomainTransferApproveFlowTest
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.build());
|
||||
@@ -417,7 +419,9 @@ class DomainTransferApproveFlowTest
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
|
||||
@@ -1339,7 +1339,9 @@ class DomainTransferRequestFlowTest
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -1398,7 +1400,9 @@ class DomainTransferRequestFlowTest
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -1456,7 +1460,9 @@ class DomainTransferRequestFlowTest
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
|
||||
@@ -162,7 +162,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
|
||||
.setRegistrant(mak21Contact.createVKey())
|
||||
.setRegistrant(Optional.of(mak21Contact.createVKey()))
|
||||
.setNameservers(ImmutableSet.of(host.createVKey()))
|
||||
.build());
|
||||
persistResource(
|
||||
@@ -338,7 +338,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.asBuilder()
|
||||
.setNameservers(nameservers.build())
|
||||
.setContacts(ImmutableSet.copyOf(contacts.subList(0, 3)))
|
||||
.setRegistrant(contacts.get(3).getContactKey())
|
||||
.setRegistrant(Optional.of(contacts.get(3).getContactKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
assertMutatingFlow(true);
|
||||
@@ -348,7 +348,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(domain.getNameservers()).hasSize(13);
|
||||
// getContacts does not return contacts of type REGISTRANT, so check these separately.
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
assertThat(loadByKey(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
|
||||
assertThat(loadByKey(domain.getRegistrant().get()).getContactId()).isEqualTo("max_test_7");
|
||||
assertNoBillingEvents();
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
@@ -426,7 +426,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013.createVKey())
|
||||
.setRegistrant(Optional.of(sh8013.createVKey()))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -441,7 +441,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(sh8013Key)
|
||||
.setRegistrant(Optional.of(sh8013Key))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Key),
|
||||
@@ -1590,7 +1590,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
|
||||
.build());
|
||||
runFlow();
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
@@ -1605,7 +1605,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("mak21"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("mak21");
|
||||
|
||||
runFlow();
|
||||
@@ -1615,7 +1615,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.forEach(
|
||||
contact ->
|
||||
assertThat(loadByKey(contact.getContactKey()).getContactId()).isEqualTo("sh8013"));
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
assertThat(loadByKey(reloadResourceByForeignKey().getRegistrant().get()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
@@ -26,16 +27,28 @@ import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
@@ -50,6 +63,9 @@ public final class OteAccountBuilderTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
|
||||
@Test
|
||||
void testGetRegistrarToTldMap() {
|
||||
assertThat(OteAccountBuilder.forRegistrarId("myclientid").getRegistrarIdToTldMap())
|
||||
@@ -89,23 +105,63 @@ public final class OteAccountBuilderTest {
|
||||
assertThat(registrar.getAllowedTlds()).containsExactly(tld);
|
||||
}
|
||||
|
||||
private static void assertContactExists(String registrarId, String email) {
|
||||
Registrar registrar = Registrar.loadByRegistrarId(registrarId).get();
|
||||
assertThat(registrar.getContacts().stream().map(RegistrarPoc::getEmailAddress)).contains(email);
|
||||
RegistrarPoc contact =
|
||||
registrar.getContacts().stream()
|
||||
.filter(c -> email.equals(c.getEmailAddress()))
|
||||
.findAny()
|
||||
.get();
|
||||
assertThat(contact.getEmailAddress()).isEqualTo(email);
|
||||
assertThat(contact.getLoginEmailAddress()).isEqualTo(email);
|
||||
public static void verifyUser(String registrarId, String email) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
assertThat(maybeUser).isPresent();
|
||||
assertThat(maybeUser.get().getUserRoles().getRegistrarRoles().get(registrarId))
|
||||
.isEqualTo(RegistrarRole.ACCOUNT_MANAGER);
|
||||
}
|
||||
|
||||
public static void verifyIapPermission(
|
||||
@Nullable String emailAddress,
|
||||
Optional<String> maybeGroupEmailAddress,
|
||||
CloudTasksHelper cloudTasksHelper,
|
||||
IamClient iamClient) {
|
||||
if (emailAddress == null) {
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(iamClient);
|
||||
} else {
|
||||
String groupEmailAddress = maybeGroupEmailAddress.orElse(null);
|
||||
if (groupEmailAddress == null) {
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verify(iamClient).addBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.service("TOOLS")
|
||||
.method(HttpMethod.POST)
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", emailAddress)
|
||||
.param("groupEmailAddress", groupEmailAddress)
|
||||
.param("groupUpdateMode", "ADD"));
|
||||
verifyNoInteractions(iamClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateUserGroup() {
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addUser("email@example.com")
|
||||
.grantIapPermission(Optional.of("console@example.com"), cloudTasksUtils, iamClient);
|
||||
verifyIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksHelper, iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGrantIndividualPermission() {
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addUser("email@example.com")
|
||||
.grantIapPermission(Optional.empty(), cloudTasksUtils, iamClient);
|
||||
verifyIapPermission("email@example.com", Optional.empty(), cloudTasksHelper, iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateOteEntities_success() {
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addContact("email@example.com")
|
||||
.buildAndPersist();
|
||||
OteAccountBuilder.forRegistrarId("myclientid").addUser("email@example.com").buildAndPersist();
|
||||
|
||||
assertTldExists("myclientid-sunrise", START_DATE_SUNRISE, Money.zero(USD));
|
||||
assertTldExists("myclientid-ga", GENERAL_AVAILABILITY, Money.zero(USD));
|
||||
@@ -114,18 +170,18 @@ public final class OteAccountBuilderTest {
|
||||
assertRegistrarExists("myclientid-3", "myclientid-ga");
|
||||
assertRegistrarExists("myclientid-4", "myclientid-ga");
|
||||
assertRegistrarExists("myclientid-5", "myclientid-eap");
|
||||
assertContactExists("myclientid-1", "email@example.com");
|
||||
assertContactExists("myclientid-3", "email@example.com");
|
||||
assertContactExists("myclientid-4", "email@example.com");
|
||||
assertContactExists("myclientid-5", "email@example.com");
|
||||
verifyUser("myclientid-1", "email@example.com");
|
||||
verifyUser("myclientid-3", "email@example.com");
|
||||
verifyUser("myclientid-4", "email@example.com");
|
||||
verifyUser("myclientid-5", "email@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateOteEntities_multipleContacts_success() {
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addContact("email@example.com")
|
||||
.addContact("other@example.com")
|
||||
.addContact("someone@example.com")
|
||||
.addUser("email@example.com")
|
||||
.addUser("other@example.com")
|
||||
.addUser("someone@example.com")
|
||||
.buildAndPersist();
|
||||
|
||||
assertTldExists("myclientid-sunrise", START_DATE_SUNRISE, Money.zero(USD));
|
||||
@@ -135,18 +191,18 @@ public final class OteAccountBuilderTest {
|
||||
assertRegistrarExists("myclientid-3", "myclientid-ga");
|
||||
assertRegistrarExists("myclientid-4", "myclientid-ga");
|
||||
assertRegistrarExists("myclientid-5", "myclientid-eap");
|
||||
assertContactExists("myclientid-1", "email@example.com");
|
||||
assertContactExists("myclientid-3", "email@example.com");
|
||||
assertContactExists("myclientid-4", "email@example.com");
|
||||
assertContactExists("myclientid-5", "email@example.com");
|
||||
assertContactExists("myclientid-1", "other@example.com");
|
||||
assertContactExists("myclientid-3", "other@example.com");
|
||||
assertContactExists("myclientid-4", "other@example.com");
|
||||
assertContactExists("myclientid-5", "other@example.com");
|
||||
assertContactExists("myclientid-1", "someone@example.com");
|
||||
assertContactExists("myclientid-3", "someone@example.com");
|
||||
assertContactExists("myclientid-4", "someone@example.com");
|
||||
assertContactExists("myclientid-5", "someone@example.com");
|
||||
verifyUser("myclientid-1", "email@example.com");
|
||||
verifyUser("myclientid-3", "email@example.com");
|
||||
verifyUser("myclientid-4", "email@example.com");
|
||||
verifyUser("myclientid-5", "email@example.com");
|
||||
verifyUser("myclientid-1", "other@example.com");
|
||||
verifyUser("myclientid-3", "other@example.com");
|
||||
verifyUser("myclientid-4", "other@example.com");
|
||||
verifyUser("myclientid-5", "other@example.com");
|
||||
verifyUser("myclientid-1", "someone@example.com");
|
||||
verifyUser("myclientid-3", "someone@example.com");
|
||||
verifyUser("myclientid-4", "someone@example.com");
|
||||
verifyUser("myclientid-5", "someone@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,7 +279,7 @@ public final class OteAccountBuilderTest {
|
||||
OteAccountBuilder oteSetupHelper = OteAccountBuilder.forRegistrarId("myclientid");
|
||||
|
||||
IllegalStateException thrown =
|
||||
assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist());
|
||||
assertThrows(IllegalStateException.class, oteSetupHelper::buildAndPersist);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Found existing object(s) conflicting with OT&E objects");
|
||||
@@ -236,7 +292,7 @@ public final class OteAccountBuilderTest {
|
||||
OteAccountBuilder oteSetupHelper = OteAccountBuilder.forRegistrarId("myclientid");
|
||||
|
||||
IllegalStateException thrown =
|
||||
assertThrows(IllegalStateException.class, () -> oteSetupHelper.buildAndPersist());
|
||||
assertThrows(IllegalStateException.class, oteSetupHelper::buildAndPersist);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Found existing object(s) conflicting with OT&E objects");
|
||||
@@ -261,7 +317,7 @@ public final class OteAccountBuilderTest {
|
||||
void testCreateOteEntities_doubleCreation_actuallyReplaces() {
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.setPassword("oldPassword")
|
||||
.addContact("email@example.com")
|
||||
.addUser("email@example.com")
|
||||
.buildAndPersist();
|
||||
|
||||
assertThat(Registrar.loadByRegistrarId("myclientid-3").get().verifyPassword("oldPassword"))
|
||||
@@ -269,7 +325,7 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.setPassword("newPassword")
|
||||
.addContact("email@example.com")
|
||||
.addUser("email@example.com")
|
||||
.setReplaceExisting(true)
|
||||
.buildAndPersist();
|
||||
|
||||
@@ -281,19 +337,17 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
@Test
|
||||
void testCreateOteEntities_doubleCreation_keepsOldContacts() {
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addContact("email@example.com")
|
||||
.buildAndPersist();
|
||||
OteAccountBuilder.forRegistrarId("myclientid").addUser("email@example.com").buildAndPersist();
|
||||
|
||||
assertContactExists("myclientid-3", "email@example.com");
|
||||
verifyUser("myclientid-3", "email@example.com");
|
||||
|
||||
OteAccountBuilder.forRegistrarId("myclientid")
|
||||
.addContact("other@example.com")
|
||||
.addUser("other@example.com")
|
||||
.setReplaceExisting(true)
|
||||
.buildAndPersist();
|
||||
|
||||
assertContactExists("myclientid-3", "other@example.com");
|
||||
assertContactExists("myclientid-3", "email@example.com");
|
||||
verifyUser("myclientid-3", "other@example.com");
|
||||
verifyUser("myclientid-3", "email@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -79,9 +79,7 @@ public final class OteStatsTestHelper {
|
||||
public static void setupIncompleteOte(String baseClientId) throws IOException {
|
||||
createTld("tld");
|
||||
persistPremiumList("default_sandbox_list", USD, "sandbox,USD 1000");
|
||||
OteAccountBuilder.forRegistrarId(baseClientId)
|
||||
.addContact("email@example.com")
|
||||
.buildAndPersist();
|
||||
OteAccountBuilder.forRegistrarId(baseClientId).buildAndPersist();
|
||||
String oteAccount1 = String.format("%s-1", baseClientId);
|
||||
DateTime now = DateTime.now(DateTimeZone.UTC);
|
||||
persistResource(
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package google.registry.model.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
@@ -42,8 +45,8 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
void testSuccess_persistence() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
@@ -58,15 +61,15 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
void testSuccess_getSingleFlag() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build();
|
||||
persistResource(featureFlag);
|
||||
assertThat(FeatureFlag.get("testFlag")).isEqualTo(featureFlag);
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE)).isEqualTo(featureFlag);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,8 +77,8 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
FeatureFlag featureFlag1 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag1")
|
||||
.setStatus(
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
@@ -84,8 +87,8 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
FeatureFlag featureFlag2 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag2")
|
||||
.setStatus(
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(3), INACTIVE)
|
||||
@@ -94,14 +97,18 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
FeatureFlag featureFlag3 =
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag3")
|
||||
.setStatus(
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
ImmutableSet<FeatureFlag> featureFlags =
|
||||
FeatureFlag.get(ImmutableSet.of("testFlag1", "testFlag2", "testFlag3"));
|
||||
FeatureFlag.getAll(
|
||||
ImmutableSet.of(
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED));
|
||||
assertThat(featureFlags.size()).isEqualTo(3);
|
||||
assertThat(featureFlags).containsExactly(featureFlag1, featureFlag2, featureFlag3);
|
||||
}
|
||||
@@ -110,8 +117,8 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
void testFailure_getMultipleFlagsOneMissing() {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag1")
|
||||
.setStatus(
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
@@ -119,8 +126,8 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag2")
|
||||
.setStatus(
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(3), INACTIVE)
|
||||
@@ -129,74 +136,48 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
FeatureFlagNotFoundException thrown =
|
||||
assertThrows(
|
||||
FeatureFlagNotFoundException.class,
|
||||
() -> FeatureFlag.get(ImmutableSet.of("missingFlag", "testFlag1", "testFlag2")));
|
||||
() ->
|
||||
FeatureFlag.getAll(
|
||||
ImmutableSet.of(
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("No feature flag object(s) found for missingFlag");
|
||||
.isEqualTo("No feature flag object(s) found for MINIMUM_DATASET_CONTACTS_PROHIBITED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_featureFlagNotPresent() {
|
||||
FeatureFlagNotFoundException thrown =
|
||||
assertThrows(FeatureFlagNotFoundException.class, () -> FeatureFlag.get("fakeFlag"));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("No feature flag object(s) found for fakeFlag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_resetFeatureName() {
|
||||
FeatureFlag featureFlag =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("testFlag")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build();
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> featureFlag.asBuilder().setFeatureName("differentName"));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name can only be set once");
|
||||
assertThrows(FeatureFlagNotFoundException.class, () -> FeatureFlag.get(TEST_FEATURE));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("No feature flag object(s) found for TEST_FEATURE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullFeatureName() {
|
||||
FeatureFlag.Builder featureFlagBuilder =
|
||||
new FeatureFlag.Builder()
|
||||
.setStatus(
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> featureFlagBuilder.build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name must not be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_emptyFeatureName() {
|
||||
FeatureFlag.Builder featureFlagBuilder =
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName("")
|
||||
.setStatus(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> featureFlagBuilder.build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Feature name must not be null or empty");
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("FeatureName cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidStatusMap() {
|
||||
FeatureFlag.Builder featureFlagBuilder = new FeatureFlag.Builder().setFeatureName("testFlag");
|
||||
FeatureFlag.Builder featureFlagBuilder = new FeatureFlag.Builder().setFeatureName(TEST_FEATURE);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
featureFlagBuilder.setStatus(
|
||||
featureFlagBuilder.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(DateTime.now(UTC).plusWeeks(8), ACTIVE)
|
||||
.build()));
|
||||
|
||||
@@ -16,11 +16,21 @@ package google.registry.model.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.tools.IamClient;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link User}. */
|
||||
@@ -104,4 +114,56 @@ public class UserTest extends EntityTestCase {
|
||||
assertThat(user.hasRegistryLockPassword()).isFalse();
|
||||
assertThat(user.verifyRegistryLockPassword("foobar")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGrantIapPermission() {
|
||||
CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
|
||||
// Individual permission.
|
||||
User.grantIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued();
|
||||
verify(iamClient).addBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.grantIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.service("TOOLS")
|
||||
.method(HttpMethod.POST)
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "email@example.com")
|
||||
.param("groupEmailAddress", "console@example.com")
|
||||
.param("groupUpdateMode", "ADD"));
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokeIapPermission() {
|
||||
CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
|
||||
// Individual permission.
|
||||
User.revokeIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued();
|
||||
verify(iamClient).removeBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.revokeIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.service("TOOLS")
|
||||
.method(HttpMethod.POST)
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "email@example.com")
|
||||
.param("groupEmailAddress", "console@example.com")
|
||||
.param("groupUpdateMode", "REMOVE"));
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationW
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -69,7 +72,7 @@ public class DomainSqlTest {
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
private Domain domain;
|
||||
private VKey<Contact> contactKey;
|
||||
private Optional<VKey<Contact>> contactKey;
|
||||
private VKey<Contact> contact2Key;
|
||||
private VKey<Host> host1VKey;
|
||||
private Host host;
|
||||
@@ -82,7 +85,7 @@ public class DomainSqlTest {
|
||||
saveRegistrar("registrar1");
|
||||
saveRegistrar("registrar2");
|
||||
saveRegistrar("registrar3");
|
||||
contactKey = createKey(Contact.class, "contact_id1");
|
||||
contactKey = Optional.of(createKey(Contact.class, "contact_id1"));
|
||||
contact2Key = createKey(Contact.class, "contact_id2");
|
||||
|
||||
host1VKey = createKey(Host.class, "host1");
|
||||
@@ -133,10 +136,12 @@ public class DomainSqlTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123Unlimited")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("dev", "app"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setTokenStatusTransitions(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
|
||||
@@ -185,7 +185,7 @@ public class DomainTest {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setRegistrant(contact1Key)
|
||||
.setRegistrant(Optional.of(contact1Key))
|
||||
.setNameservers(ImmutableSet.of(hostKey))
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
@@ -242,6 +242,16 @@ public class DomainTest {
|
||||
.hasValue(domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrantNotRequired() {
|
||||
persistResource(domain.asBuilder().setRegistrant(Optional.empty()).build());
|
||||
assertThat(
|
||||
loadByForeignKey(Domain.class, domain.getForeignKey(), fakeClock.nowUtc())
|
||||
.get()
|
||||
.getRegistrant())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyStringsBecomeNull() {
|
||||
assertThat(
|
||||
@@ -832,7 +842,9 @@ public class DomainTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -894,7 +906,9 @@ public class DomainTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -1012,17 +1026,17 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
true);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).hasValue(contact2Key);
|
||||
assertThat(domain.getBillingContact()).hasValue(contact3Key);
|
||||
assertThat(domain.getTechContact()).hasValue(contact4Key);
|
||||
|
||||
// Make sure everything gets nulled out.
|
||||
domain.setContactFields(ImmutableSet.of(), true);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).isEmpty();
|
||||
assertThat(domain.getBillingContact()).isEmpty();
|
||||
assertThat(domain.getTechContact()).isEmpty();
|
||||
|
||||
// Make sure that changes don't affect the registrant unless requested.
|
||||
domain.setContactFields(
|
||||
@@ -1032,16 +1046,16 @@ public class DomainTest {
|
||||
DesignatedContact.create(Type.BILLING, contact3Key),
|
||||
DesignatedContact.create(Type.TECH, contact4Key)),
|
||||
false);
|
||||
assertThat(domain.getRegistrant()).isNull();
|
||||
assertThat(domain.getAdminContact()).isEqualTo(contact2Key);
|
||||
assertThat(domain.getBillingContact()).isEqualTo(contact3Key);
|
||||
assertThat(domain.getTechContact()).isEqualTo(contact4Key);
|
||||
domain = domain.asBuilder().setRegistrant(contact1Key).build();
|
||||
assertThat(domain.getRegistrant()).isEmpty();
|
||||
assertThat(domain.getAdminContact()).hasValue(contact2Key);
|
||||
assertThat(domain.getBillingContact()).hasValue(contact3Key);
|
||||
assertThat(domain.getTechContact()).hasValue(contact4Key);
|
||||
domain = domain.asBuilder().setRegistrant(Optional.of(contact1Key)).build();
|
||||
domain.setContactFields(ImmutableSet.of(), false);
|
||||
assertThat(domain.getRegistrant()).isEqualTo(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isNull();
|
||||
assertThat(domain.getBillingContact()).isNull();
|
||||
assertThat(domain.getTechContact()).isNull();
|
||||
assertThat(domain.getRegistrant()).hasValue(contact1Key);
|
||||
assertThat(domain.getAdminContact()).isEmpty();
|
||||
assertThat(domain.getBillingContact()).isEmpty();
|
||||
assertThat(domain.getTechContact()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1064,7 +1078,9 @@ public class DomainTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build();
|
||||
@@ -1082,7 +1098,9 @@ public class DomainTest {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
|
||||
@@ -43,6 +43,8 @@ import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -158,15 +160,16 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testSetRenewalBehavior_assertsRenewalBehaviorIsNotDefault() {
|
||||
assertThat(
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build())
|
||||
.getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 5))
|
||||
.build());
|
||||
assertThat(token.getRenewalPriceBehavior()).isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
assertThat(token.getRenewalPrice()).hasValue(Money.of(CurrencyUnit.USD, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,7 +184,11 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
AllocationToken loadedToken = loadByEntity(token);
|
||||
assertThat(token).isEqualTo(loadedToken);
|
||||
persistResource(
|
||||
loadedToken.asBuilder().setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED).build());
|
||||
loadedToken
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 5))
|
||||
.build());
|
||||
assertThat(loadByEntity(token).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.SPECIFIED);
|
||||
}
|
||||
@@ -218,17 +225,50 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_bulkTokenNotSpecifiedRenewalBehavior() {
|
||||
void testFail_bulkTokenInvalidRenewalBehavior() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
.build()))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("BULK_PRICING tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 5))
|
||||
.build()))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("BULK_PRICING tokens must have a renewal price of 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_bulkTokenDiscountFraction() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT);
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Bulk tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
.isEqualTo("BULK_PRICING tokens must have a discountFraction of 1.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -237,12 +277,14 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.RESTORE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED);
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Bulk tokens may only be valid for CREATE actions");
|
||||
.isEqualTo("BULK_PRICING tokens may only be valid for CREATE actions");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -263,11 +305,13 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED);
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Bulk tokens may only be valid for CREATE actions");
|
||||
.isEqualTo("BULK_PRICING tokens may only be valid for CREATE actions");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -276,11 +320,15 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setDiscountPremiums(true);
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Bulk tokens cannot discount premium names");
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("BULK_PRICING tokens cannot discount premium names");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -338,8 +386,10 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
new AllocationToken.Builder()
|
||||
.setToken("foobar")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("foo", "bar"));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
@@ -536,6 +586,32 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.isEqualTo("Discount years can only be specified along with a discount fraction");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_specifiedTokenInvalidBehavior() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.build()))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 10))
|
||||
.build()))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_registrationBehaviors() {
|
||||
createTld("tld");
|
||||
@@ -564,6 +640,35 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailures_badNonpremiumCreate() {
|
||||
createTld("tld");
|
||||
AllocationToken validToken =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc")
|
||||
.setTokenType(SINGLE_USE)
|
||||
.setRegistrationBehavior(RegistrationBehavior.NONPREMIUM_CREATE)
|
||||
.setDomainName("example.tld")
|
||||
.build();
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> validToken.asBuilder().setDomainName(null).build());
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> validToken.asBuilder().setDiscountFraction(0.5).build());
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
validToken
|
||||
.asBuilder()
|
||||
.setAllowedEppActions(
|
||||
ImmutableSet.of(
|
||||
CommandName.RENEW,
|
||||
CommandName.TRANSFER,
|
||||
CommandName.RESTORE,
|
||||
CommandName.UPDATE))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void assertBadInitialTransition(TokenStatus status) {
|
||||
assertBadTransition(
|
||||
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
|
||||
|
||||
@@ -55,8 +55,9 @@ public class BulkPricingPackageTest extends EntityTestCase {
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
@@ -84,7 +85,7 @@ public class BulkPricingPackageTest extends EntityTestCase {
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -68,7 +69,7 @@ public final class Spec11ThreatMatchTest extends EntityTestCase {
|
||||
.setDomainName("foo.tld")
|
||||
.setRepoId(domainRepoId)
|
||||
.setNameservers(hostVKey)
|
||||
.setRegistrant(registrantContactVKey)
|
||||
.setRegistrant(Optional.of(registrantContactVKey))
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build();
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
@@ -26,6 +27,7 @@ import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarPo
|
||||
import static google.registry.testing.GsonSubject.assertAboutJson;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -278,6 +280,33 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_evenWhenRegistrantDoesntExist() {
|
||||
// Even though the registrant is empty on this domain, it still shows a full set of REDACTED
|
||||
// fields through RDAP.
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_whenNoContactsExist() {
|
||||
// Even though the domain has no contacts, it still shows a full set of REDACTED fields through
|
||||
// RDAP.
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_exist_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_loggedInAsOtherRegistrar_noContacts() {
|
||||
login("idnregistrar");
|
||||
|
||||
@@ -52,6 +52,8 @@ import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
|
||||
import google.registry.rdap.RdapObjectClasses.TopLevelReplyObject;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -77,7 +79,7 @@ class RdapJsonFormatterTest {
|
||||
private Host hostNoAddresses;
|
||||
private Host hostNotLinked;
|
||||
private Host hostSuperordinatePendingTransfer;
|
||||
private Contact contactRegistrant;
|
||||
@Nullable private Contact contactRegistrant;
|
||||
private Contact contactAdmin;
|
||||
private Contact contactTech;
|
||||
private Contact contactNotLinked;
|
||||
@@ -371,7 +373,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -384,7 +386,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.SUMMARY)
|
||||
.toJson())
|
||||
@@ -398,7 +400,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -418,7 +420,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactRegistrant,
|
||||
Optional.of(contactRegistrant),
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -431,7 +433,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactAdmin, ImmutableSet.of(RdapEntity.Role.ADMIN), OutputDataType.FULL)
|
||||
Optional.of(contactAdmin),
|
||||
ImmutableSet.of(RdapEntity.Role.ADMIN),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_admincontact.json"));
|
||||
}
|
||||
@@ -442,7 +446,9 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
contactTech, ImmutableSet.of(RdapEntity.Role.TECH), OutputDataType.FULL)
|
||||
Optional.of(contactTech),
|
||||
ImmutableSet.of(RdapEntity.Role.TECH),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_techcontact.json"));
|
||||
}
|
||||
@@ -452,7 +458,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactTech, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactTech), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_rolelesscontact.json"));
|
||||
}
|
||||
@@ -462,7 +469,8 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(contactNotLinked, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactNotLinked), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_unlinkedcontact.json"));
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import google.registry.xjc.rdedomain.XjcRdeDomainElement;
|
||||
import google.registry.xjc.rgp.XjcRgpStatusType;
|
||||
import google.registry.xjc.secdns.XjcSecdnsDsDataType;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -280,9 +281,14 @@ public class DomainToXjcConverterTest {
|
||||
makeHost(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef")
|
||||
.createVKey()))
|
||||
.setRegistrant(
|
||||
makeContact(
|
||||
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(
|
||||
clock,
|
||||
"12-Q9JYB4C",
|
||||
"5372808-ERL",
|
||||
"(◕‿◕) nevermore",
|
||||
"prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z"))
|
||||
.setGracePeriods(
|
||||
ImmutableSet.of(
|
||||
|
||||
@@ -51,6 +51,7 @@ import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -63,8 +64,9 @@ final class RdeFixtures {
|
||||
.setDomainName("example." + tld)
|
||||
.setRepoId(generateNewDomainRoid(tld))
|
||||
.setRegistrant(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey())
|
||||
Optional.of(
|
||||
makeContact(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
|
||||
.createVKey()))
|
||||
.build();
|
||||
DomainHistory historyEntry =
|
||||
persistResource(
|
||||
|
||||
@@ -135,7 +135,7 @@ public final class RegistryTestServerMain {
|
||||
|
||||
final RegistryTestServer server = new RegistryTestServer(address);
|
||||
|
||||
System.out.printf("%sLoading SQL fixtures and User service...%s\n", BLUE, RESET);
|
||||
System.out.printf("%sLoading SQL fixtures setting User for authentication...%s\n", BLUE, RESET);
|
||||
UserRoles userRoles =
|
||||
new UserRoles.Builder().setIsAdmin(loginIsAdmin).setGlobalRole(GlobalRole.FTE).build();
|
||||
User user =
|
||||
@@ -151,7 +151,7 @@ public final class RegistryTestServerMain {
|
||||
for (Fixture fixture : fixtures) {
|
||||
fixture.load();
|
||||
}
|
||||
System.out.printf("%sStarting Jetty6 HTTP Server...%s\n", BLUE, RESET);
|
||||
System.out.printf("%sStarting Jetty HTTP Server...%s\n", BLUE, RESET);
|
||||
server.start();
|
||||
System.out.printf("%sListening on: %s%s\n", PURPLE, server.getUrl("/"), RESET);
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import com.google.common.util.concurrent.SimpleTimeLimiter;
|
||||
import google.registry.ui.server.registrar.RegistrarSettingsAction;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.util.UrlChecker;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -91,7 +91,7 @@ public final class TestServer {
|
||||
/** Starts the HTTP server in a new thread and returns once it's online. */
|
||||
public void start() {
|
||||
try {
|
||||
RegistrarSettingsAction.setIsInTestDriverToTrue();
|
||||
RegistryEnvironment.setIsInTestDriver(true);
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
throwIfUnchecked(e);
|
||||
@@ -129,7 +129,7 @@ public final class TestServer {
|
||||
.callWithTimeout(
|
||||
() -> {
|
||||
server.stop();
|
||||
RegistrarSettingsAction.setIsInTestDriverToFalse();
|
||||
RegistryEnvironment.setIsInTestDriver(false);
|
||||
return null;
|
||||
},
|
||||
SHUTDOWN_TIMEOUT_MS,
|
||||
|
||||
@@ -80,12 +80,12 @@ import org.joda.time.DateTime;
|
||||
* to the same test task container that the original instance pushes to, so that we can make
|
||||
* assertions on them by accessing the original instance. We cannot make the test task container
|
||||
* itself static because we do not want tasks enqueued in previous tests to interfere with latter
|
||||
* tests, when they run on the same JVM (and therefore share the same static class members). To
|
||||
* solve this we put the test container in a static map whose keys are the instance IDs. An
|
||||
* explicitly created new {@link CloudTasksHelper} (as would be created for a new test method) would
|
||||
* have a new ID allocated to it, and therefore stores its tasks in a distinct container. A
|
||||
* deserialized {@link CloudTasksHelper}, on the other hand, will have the same instance ID and
|
||||
* share the same test class container with its progenitor.
|
||||
* tests when they run on the same JVM (and therefore share the same static class members). To solve
|
||||
* this, we put the test container in a static map whose keys are the instance IDs. An explicitly
|
||||
* created new {@link CloudTasksHelper} (as would be created for a new test method) would have a new
|
||||
* ID allocated to it, and therefore stores its tasks in a distinct container. A deserialized {@link
|
||||
* CloudTasksHelper}, on the other hand, will have the same instance ID and share the same test
|
||||
* class container with its progenitor.
|
||||
*/
|
||||
public class CloudTasksHelper implements Serializable {
|
||||
|
||||
@@ -131,7 +131,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
*/
|
||||
public void assertTasksEnqueuedWithProperty(
|
||||
String queueName, Function<Task, String> propertyGetter, String... expectedTaskProperties) {
|
||||
// Ordering is irrelevant but duplicates should be considered independently.
|
||||
// Ordering is irrelevant, but duplicates should be considered independently.
|
||||
assertThat(getTestTasksFor(queueName).stream().map(propertyGetter))
|
||||
.containsExactly((Object[]) expectedTaskProperties);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
});
|
||||
headers = headerBuilder.build();
|
||||
ImmutableMultimap.Builder<String, String> paramBuilder = new ImmutableMultimap.Builder<>();
|
||||
// Note that UriParameters.parse() does not throw an IAE on a bad query string (e.g. one
|
||||
// Note that UriParameters.parse() does not throw an IAE on a bad query string (e.g., one
|
||||
// where parameters are not properly URL-encoded); it always does a best-effort parse.
|
||||
if (method == HttpMethod.GET && uri.getQuery() != null) {
|
||||
paramBuilder.putAll(UriParameters.parse(uri.getQuery()));
|
||||
@@ -382,7 +382,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
* the same contract as {@link #equals}, since it will ignore null fields.
|
||||
*
|
||||
* <p>Match fails if any headers or params expected on the TaskMatcher are not found on the
|
||||
* Task. Note that the inverse is not true (i.e. there may be extra headers on the Task).
|
||||
* Task. Note that the inverse is not true (i.e., there may be extra headers on the Task).
|
||||
*
|
||||
* <p>Schedule time by default is Timestamp.getDefaultInstance() or null.
|
||||
*/
|
||||
|
||||
@@ -190,7 +190,7 @@ public final class DatabaseHelper {
|
||||
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(START_OF_TIME)
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
|
||||
.setRegistrant(contactKey)
|
||||
.setRegistrant(Optional.of(contactKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contactKey),
|
||||
@@ -603,7 +603,7 @@ public final class DatabaseHelper {
|
||||
.setCreationRegistrarId("TheRegistrar")
|
||||
.setCreationTimeForTest(creationTime)
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrant(Optional.of(contact.createVKey()))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.common.base.Throwables;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.request.Response;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
@@ -69,7 +70,7 @@ public final class FakeResponse implements Response {
|
||||
|
||||
@Override
|
||||
public void sendRedirect(String url) throws IOException {
|
||||
status = 302;
|
||||
status = HttpServletResponse.SC_FOUND;
|
||||
this.payload = String.format("Redirected to %s", url);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.util.Idn;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -351,7 +352,7 @@ public final class FullFieldsTestEntityHelper {
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, "deadface")));
|
||||
if (registrant != null) {
|
||||
builder.setRegistrant(registrant.createVKey());
|
||||
builder.setRegistrant(Optional.of(registrant.createVKey()));
|
||||
}
|
||||
if ((admin != null) || (tech != null)) {
|
||||
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright 2024 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.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link ConfigureFeatureFlagCommand}. */
|
||||
public class ConfigureFeatureFlagCommandTest extends CommandTestCase<ConfigureFeatureFlagCommand> {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01T00:00:00Z"));
|
||||
|
||||
@Test
|
||||
void testCreate() throws Exception {
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(2);
|
||||
|
||||
runCommandForced(
|
||||
"TEST_FEATURE",
|
||||
"--status_map",
|
||||
String.format("%s=INACTIVE,%s=ACTIVE", START_OF_TIME, featureStart));
|
||||
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_multipleFlags() throws Exception {
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(2);
|
||||
|
||||
runCommandForced(
|
||||
"TEST_FEATURE",
|
||||
"MINIMUM_DATASET_CONTACTS_OPTIONAL",
|
||||
"MINIMUM_DATASET_CONTACTS_PROHIBITED",
|
||||
"--status_map",
|
||||
String.format("%s=INACTIVE,%s=ACTIVE", START_OF_TIME, featureStart));
|
||||
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.get(MINIMUM_DATASET_CONTACTS_PROHIBITED).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(6);
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(1);
|
||||
|
||||
runCommandForced(
|
||||
"TEST_FEATURE",
|
||||
"--status_map",
|
||||
String.format("%s=INACTIVE,%s=ACTIVE", START_OF_TIME, featureStart));
|
||||
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfigure_multipleFlags() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(6);
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(1);
|
||||
|
||||
runCommandForced(
|
||||
"TEST_FEATURE",
|
||||
"MINIMUM_DATASET_CONTACTS_OPTIONAL",
|
||||
"MINIMUM_DATASET_CONTACTS_PROHIBITED",
|
||||
"--status_map",
|
||||
String.format("%s=INACTIVE,%s=ACTIVE", START_OF_TIME, featureStart));
|
||||
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.get(MINIMUM_DATASET_CONTACTS_PROHIBITED).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, featureStart, ACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_invalidFeatureName() throws Exception {
|
||||
ParameterException thrown =
|
||||
assertThrows(
|
||||
ParameterException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"INVALID_NAME", "--status_map", String.format("%s=ACTIVE", START_OF_TIME)));
|
||||
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Invalid value for [Main class] parameter. Allowed values");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_invalidStatusMap() throws Exception {
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(2);
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"TEST_FEATURE", "--status_map", String.format("%s=ACTIVE", featureStart)));
|
||||
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must provide transition entry for the start of time (Unix Epoch)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_invalidStatusMap() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
DateTime featureStart = clock.nowUtc().plusWeeks(6);
|
||||
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
|
||||
.isEqualTo(
|
||||
TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE)));
|
||||
assertThat(FeatureFlag.getAllUncached()).hasSize(1);
|
||||
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"TEST_FEATURE", "--status_map", String.format("%s=ACTIVE", featureStart)));
|
||||
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must provide transition entry for the start of time (Unix Epoch)");
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -46,8 +47,9 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--max_domains=100",
|
||||
@@ -78,7 +80,8 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -122,8 +125,9 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--max_domains=100",
|
||||
@@ -158,8 +162,9 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
runCommandForced("--price=USD 1000.00", "--next_billing_date=2012-03-17T00:00:00Z", "abc123");
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
@@ -184,8 +189,9 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
runCommandForced("--max_domains=100", "--max_creates=500", "--price=USD 1000.00", "abc123");
|
||||
|
||||
@@ -210,8 +216,9 @@ public class CreateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
|
||||
@@ -15,20 +15,22 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.tools.CreateUserCommand.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -38,13 +40,13 @@ import org.junit.jupiter.api.Test;
|
||||
public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final ServiceConnection connection = mock(ServiceConnection.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.iamClient = iamClient;
|
||||
command.maybeGroupEmailAddress = Optional.empty();
|
||||
command.setConnection(connection);
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -57,7 +59,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,20 +71,16 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
assertThat(onlyUser.getUserRoles().isAdmin()).isFalse();
|
||||
assertThat(onlyUser.getUserRoles().getGlobalRole()).isEqualTo(GlobalRole.NONE);
|
||||
assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
"/_dr/admin/updateUserGroup",
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"user@example.test",
|
||||
"groupEmailAddress",
|
||||
"group@example.test",
|
||||
"groupUpdateMode",
|
||||
"ADD"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.method(HttpMethod.POST)
|
||||
.service("TOOLS")
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "user@example.test")
|
||||
.param("groupEmailAddress", "group@example.test")
|
||||
.param("groupUpdateMode", "ADD"));
|
||||
verifyNoInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,7 +100,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,7 +110,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
.isEqualTo(GlobalRole.FTE);
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -131,7 +129,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
RegistrarRole.PRIMARY_CONTACT));
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +144,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A user with email user@example.test already exists");
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.tools.CreateUserCommand.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -34,13 +35,13 @@ import org.junit.jupiter.api.Test;
|
||||
public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final ServiceConnection connection = mock(ServiceConnection.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.iamClient = iamClient;
|
||||
command.setConnection(connection);
|
||||
command.maybeGroupEmailAddress = Optional.empty();
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -51,7 +52,7 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
|
||||
verify(iamClient).removeBinding("email@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -61,20 +62,16 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
assertThat(UserDao.loadUser("email@example.test")).isPresent();
|
||||
runCommandForced("--email", "email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
"/_dr/admin/updateUserGroup",
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"email@example.test",
|
||||
"groupEmailAddress",
|
||||
"group@example.test",
|
||||
"groupUpdateMode",
|
||||
"REMOVE"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.method(HttpMethod.POST)
|
||||
.service("TOOLS")
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "email@example.test")
|
||||
.param("groupEmailAddress", "group@example.test")
|
||||
.param("groupUpdateMode", "REMOVE"));
|
||||
verifyNoInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,6 +83,6 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Email does not correspond to a valid user");
|
||||
verifyNoInteractions(iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ import google.registry.util.StringGenerator.Alphabets;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -194,19 +196,47 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
|
||||
@Test
|
||||
void testSuccess_renewalPriceBehaviorIsSpecified() throws Exception {
|
||||
runCommand("--tokens", "foobar,foobaz", "--renewal_price_behavior", "SPECIFIED");
|
||||
runCommand(
|
||||
"--tokens",
|
||||
"foobar,foobaz",
|
||||
"--renewal_price_behavior",
|
||||
"SPECIFIED",
|
||||
"--renewal_price",
|
||||
"USD 10");
|
||||
assertAllocationTokens(
|
||||
createToken("foobar", null, null).asBuilder().setRenewalPriceBehavior(SPECIFIED).build(),
|
||||
createToken("foobaz", null, null).asBuilder().setRenewalPriceBehavior(SPECIFIED).build());
|
||||
createToken("foobar", null, null)
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 10))
|
||||
.build(),
|
||||
createToken("foobaz", null, null)
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 10))
|
||||
.build());
|
||||
assertInStdout("foobar", "foobaz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_renewalPriceBehaviorIsSpecifiedButMixedCase() throws Exception {
|
||||
runCommand("--tokens", "foobar,foobaz", "--renewal_price_behavior", "speCIFied");
|
||||
runCommand(
|
||||
"--tokens",
|
||||
"foobar,foobaz",
|
||||
"--renewal_price_behavior",
|
||||
"speCIFied",
|
||||
"--renewal_price",
|
||||
"USD 10");
|
||||
assertAllocationTokens(
|
||||
createToken("foobar", null, null).asBuilder().setRenewalPriceBehavior(SPECIFIED).build(),
|
||||
createToken("foobaz", null, null).asBuilder().setRenewalPriceBehavior(SPECIFIED).build());
|
||||
createToken("foobar", null, null)
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 10))
|
||||
.build(),
|
||||
createToken("foobaz", null, null)
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 10))
|
||||
.build());
|
||||
assertInStdout("foobar", "foobaz");
|
||||
}
|
||||
|
||||
@@ -236,6 +266,16 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
+ " NONPREMIUM, SPECIFIED]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_specifiedPrice_withoutPrice() throws Exception {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> runCommand("--tokens", "foobar", "--renewal_price_behavior", "SPECIFIED")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("renewal_price must be specified iff renewal_price_behavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_defaultRegistrationBehavior() throws Exception {
|
||||
runCommand("--tokens", "foobar,blah");
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.tools;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
@@ -52,8 +53,9 @@ public class GetBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -79,8 +81,9 @@ public class GetBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
@@ -102,8 +105,9 @@ public class GetBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright 2024 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.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.EntityYamlUtils;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureFlagNotFoundException;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link GetFeatureFlagCommand}. */
|
||||
public class GetFeatureFlagCommandTest extends CommandTestCase<GetFeatureFlagCommand> {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01T00:00:00Z"));
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.objectMapper = EntityYamlUtils.createObjectMapper();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(clock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
runCommand("TEST_FEATURE");
|
||||
assertInStdout(
|
||||
"featureName: \"TEST_FEATURE\"\n"
|
||||
+ "status:\n"
|
||||
+ " \"1970-01-01T00:00:00.000Z\": \"INACTIVE\"\n"
|
||||
+ " \"2000-02-26T00:00:00.000Z\": \"ACTIVE\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_multipleArguments() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(clock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(clock.nowUtc().plusWeeks(3), ACTIVE)
|
||||
.put(clock.nowUtc().plusWeeks(6), INACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
runCommand("TEST_FEATURE", "MINIMUM_DATASET_CONTACTS_OPTIONAL");
|
||||
assertInStdout(
|
||||
"featureName: \"TEST_FEATURE\"\n"
|
||||
+ "status:\n"
|
||||
+ " \"1970-01-01T00:00:00.000Z\": \"INACTIVE\"\n"
|
||||
+ " \"2000-02-26T00:00:00.000Z\": \"ACTIVE\""
|
||||
+ "\n\n"
|
||||
+ "featureName: \"MINIMUM_DATASET_CONTACTS_OPTIONAL\"\n"
|
||||
+ "status:\n"
|
||||
+ " \"1970-01-01T00:00:00.000Z\": \"INACTIVE\"\n"
|
||||
+ " \"2000-01-22T00:00:00.000Z\": \"ACTIVE\"\n"
|
||||
+ " \"2000-02-12T00:00:00.000Z\": \"INACTIVE\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_featureFlagDoesNotExist() {
|
||||
FeatureFlagNotFoundException thrown =
|
||||
assertThrows(FeatureFlagNotFoundException.class, () -> runCommand("TEST_FEATURE"));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("No feature flag object(s) found for TEST_FEATURE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_oneFlagDoesNotExist() {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(clock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
assertThrows(
|
||||
FeatureFlagNotFoundException.class,
|
||||
() -> runCommand("TEST_FEATURE", "MINIMUM_DATASET_CONTACTS_OPTIONAL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_noTldName() {
|
||||
assertThrows(ParameterException.class, this::runCommand);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2024 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 google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.TestDataHelper.loadFile;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.EntityYamlUtils;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.common.FeatureFlag.FeatureStatus;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ListFeatureFlagsCommandTest extends CommandTestCase<ListFeatureFlagsCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.mapper = EntityYamlUtils.createObjectMapper();
|
||||
fakeClock.setTo(DateTime.parse("1984-12-21T06:07:08.789Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oneFlag() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
runCommand();
|
||||
assertInStdout(loadFile(getClass(), "oneFlag.yaml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_success_manyFlags() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(1), ACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(8), INACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(10), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
runCommand();
|
||||
assertInStdout(loadFile(getClass(), "threeFlags.yaml"));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.OteAccountBuilderTest.verifyUser;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.model.registrar.RegistrarBase.State.ACTIVE;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
@@ -26,19 +28,30 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
@@ -50,22 +63,24 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
|
||||
private static final String PASSWORD = "abcdefghijklmnop";
|
||||
|
||||
private DeterministicStringGenerator passwordGenerator =
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final DeterministicStringGenerator passwordGenerator =
|
||||
new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.passwordGenerator = passwordGenerator;
|
||||
command.clock = new FakeClock(DateTime.parse("2018-07-07TZ"));
|
||||
command.maybeGroupEmailAddress = Optional.of("group@example.com");
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
command.iamClient = iamClient;
|
||||
persistPremiumList("default_sandbox_list", USD, "sandbox,USD 1000");
|
||||
}
|
||||
|
||||
/** Verify TLD creation. */
|
||||
private void verifyTldCreation(
|
||||
String tldName,
|
||||
String roidSuffix,
|
||||
TldState tldState,
|
||||
boolean isEarlyAccess) {
|
||||
String tldName, String roidSuffix, TldState tldState, boolean isEarlyAccess) {
|
||||
Tld registry = Tld.get(tldName);
|
||||
assertThat(registry).isNotNull();
|
||||
assertThat(registry.getRoidSuffix()).isEqualTo(roidSuffix);
|
||||
@@ -107,13 +122,28 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
assertThat(registrar.getClientCertificateHash()).hasValue(SAMPLE_CERT_HASH);
|
||||
}
|
||||
|
||||
private void verifyRegistrarContactCreation(String registrarName, String email) {
|
||||
ImmutableSet<RegistrarPoc> registrarPocs = loadRegistrar(registrarName).getContacts();
|
||||
assertThat(registrarPocs).hasSize(1);
|
||||
RegistrarPoc registrarPoc = registrarPocs.stream().findAny().get();
|
||||
assertThat(registrarPoc.getEmailAddress()).isEqualTo(email);
|
||||
assertThat(registrarPoc.getName()).isEqualTo(email);
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isEqualTo(email);
|
||||
private void verifyIapPermission(@Nullable String emailAddress) {
|
||||
if (emailAddress == null) {
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(iamClient);
|
||||
} else {
|
||||
String groupEmailAddress = command.maybeGroupEmailAddress.orElse(null);
|
||||
if (groupEmailAddress == null) {
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verify(iamClient).addBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.service("TOOLS")
|
||||
.method(HttpMethod.POST)
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", emailAddress)
|
||||
.param("groupEmailAddress", groupEmailAddress)
|
||||
.param("groupUpdateMode", "ADD"));
|
||||
verifyNoInteractions(iamClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,10 +166,12 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
verifyRegistrarCreation("blobio-4", "blobio-ga", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-5", "blobio-eap", PASSWORD, ipAddress);
|
||||
|
||||
verifyRegistrarContactCreation("blobio-1", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-3", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-4", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-5", "contact@email.com");
|
||||
verifyUser("blobio-1", "contact@email.com");
|
||||
verifyUser("blobio-3", "contact@email.com");
|
||||
verifyUser("blobio-4", "contact@email.com");
|
||||
verifyUser("blobio-5", "contact@email.com");
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,10 +194,12 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
verifyRegistrarCreation("abc-4", "abc-ga", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("abc-5", "abc-eap", PASSWORD, ipAddress);
|
||||
|
||||
verifyRegistrarContactCreation("abc-1", "abc@email.com");
|
||||
verifyRegistrarContactCreation("abc-3", "abc@email.com");
|
||||
verifyRegistrarContactCreation("abc-4", "abc@email.com");
|
||||
verifyRegistrarContactCreation("abc-5", "abc@email.com");
|
||||
verifyUser("abc-1", "abc@email.com");
|
||||
verifyUser("abc-3", "abc@email.com");
|
||||
verifyUser("abc-4", "abc@email.com");
|
||||
verifyUser("abc-5", "abc@email.com");
|
||||
|
||||
verifyIapPermission("abc@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -180,19 +214,20 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
verifyTldCreation("blobio-ga", "BLOBIOG2", GENERAL_AVAILABILITY, false);
|
||||
verifyTldCreation("blobio-eap", "BLOBIOE3", GENERAL_AVAILABILITY, true);
|
||||
|
||||
ImmutableList<CidrAddressBlock> ipAddresses = ImmutableList.of(
|
||||
CidrAddressBlock.create("1.1.1.1"),
|
||||
CidrAddressBlock.create("2.2.2.2"));
|
||||
ImmutableList<CidrAddressBlock> ipAddresses =
|
||||
ImmutableList.of(CidrAddressBlock.create("1.1.1.1"), CidrAddressBlock.create("2.2.2.2"));
|
||||
|
||||
verifyRegistrarCreation("blobio-1", "blobio-sunrise", PASSWORD, ipAddresses);
|
||||
verifyRegistrarCreation("blobio-3", "blobio-ga", PASSWORD, ipAddresses);
|
||||
verifyRegistrarCreation("blobio-4", "blobio-ga", PASSWORD, ipAddresses);
|
||||
verifyRegistrarCreation("blobio-5", "blobio-eap", PASSWORD, ipAddresses);
|
||||
|
||||
verifyRegistrarContactCreation("blobio-1", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-3", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-4", "contact@email.com");
|
||||
verifyRegistrarContactCreation("blobio-5", "contact@email.com");
|
||||
verifyUser("blobio-1", "contact@email.com");
|
||||
verifyUser("blobio-3", "contact@email.com");
|
||||
verifyUser("blobio-4", "contact@email.com");
|
||||
verifyUser("blobio-5", "contact@email.com");
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,6 +241,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("option is required: [-a | --ip_allow_list]");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -219,6 +255,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("option is required: [-r | --registrar]");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -232,6 +269,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Must specify exactly one client certificate file.");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,6 +283,36 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--certfile=" + getCertFilename(),
|
||||
"--registrar=blobio"));
|
||||
assertThat(thrown).hasMessageThat().contains("option is required: [--email]");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noConsoleUserGroup() throws Exception {
|
||||
command.maybeGroupEmailAddress = Optional.empty();
|
||||
runCommandForced(
|
||||
"--ip_allow_list=1.1.1.1",
|
||||
"--registrar=blobio",
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename());
|
||||
|
||||
verifyTldCreation("blobio-sunrise", "BLOBIOS0", START_DATE_SUNRISE, false);
|
||||
verifyTldCreation("blobio-ga", "BLOBIOG2", GENERAL_AVAILABILITY, false);
|
||||
verifyTldCreation("blobio-eap", "BLOBIOE3", GENERAL_AVAILABILITY, true);
|
||||
|
||||
ImmutableList<CidrAddressBlock> ipAddress =
|
||||
ImmutableList.of(CidrAddressBlock.create("1.1.1.1"));
|
||||
|
||||
verifyRegistrarCreation("blobio-1", "blobio-sunrise", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-3", "blobio-ga", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-4", "blobio-ga", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-5", "blobio-eap", PASSWORD, ipAddress);
|
||||
|
||||
verifyUser("blobio-1", "contact@email.com");
|
||||
verifyUser("blobio-3", "contact@email.com");
|
||||
verifyUser("blobio-4", "contact@email.com");
|
||||
verifyUser("blobio-5", "contact@email.com");
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -259,6 +327,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=/dev/null"));
|
||||
assertThat(thrown).hasMessageThat().contains("No X509Certificate found");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -273,6 +342,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("Invalid registrar name: 3blo-bio");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -287,6 +357,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("Invalid registrar name: bl");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -301,6 +372,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("Invalid registrar name: blobiotoooolong");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -315,6 +387,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("Invalid registrar name: blo#bio");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -330,6 +403,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("VKey<Tld>(sql:blobio-sunrise)");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -345,6 +419,8 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
|
||||
verifyTldCreation("blobio-sunrise", "BLOBIOS0", START_DATE_SUNRISE, false);
|
||||
verifyTldCreation("blobio-ga", "BLOBIOG2", GENERAL_AVAILABILITY, false);
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -366,6 +442,28 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("VKey<Registrar>(sql:blobio-1)");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_userExists() {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("contact@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
runCommandForced(
|
||||
"--ip_allow_list=1.1.1.1",
|
||||
"--registrar=blobio",
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename()));
|
||||
assertThat(thrown).hasMessageThat().contains("Found existing users: {contact@email.com");
|
||||
verifyIapPermission(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -385,10 +483,46 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename());
|
||||
|
||||
ImmutableList<CidrAddressBlock> ipAddress = ImmutableList.of(
|
||||
CidrAddressBlock.create("1.1.1.1"));
|
||||
ImmutableList<CidrAddressBlock> ipAddress =
|
||||
ImmutableList.of(CidrAddressBlock.create("1.1.1.1"));
|
||||
|
||||
verifyRegistrarCreation("blobio-1", "blobio-sunrise", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-3", "blobio-ga", PASSWORD, ipAddress);
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_userExists_replaceExisting() throws Exception {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("contact@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
|
||||
runCommandForced(
|
||||
"--overwrite",
|
||||
"--ip_allow_list=1.1.1.1",
|
||||
"--registrar=blobio",
|
||||
"--email=contact@email.com",
|
||||
"--certfile=" + getCertFilename());
|
||||
|
||||
ImmutableList<CidrAddressBlock> ipAddress =
|
||||
ImmutableList.of(CidrAddressBlock.create("1.1.1.1"));
|
||||
|
||||
verifyRegistrarCreation("blobio-1", "blobio-sunrise", PASSWORD, ipAddress);
|
||||
verifyRegistrarCreation("blobio-3", "blobio-ga", PASSWORD, ipAddress);
|
||||
|
||||
verifyUser("blobio-1", "contact@email.com");
|
||||
verifyUser("blobio-3", "contact@email.com");
|
||||
verifyUser("blobio-4", "contact@email.com");
|
||||
verifyUser("blobio-5", "contact@email.com");
|
||||
|
||||
// verify that the role is completely replaced, e.g., the global role is gone.
|
||||
assertThat(UserDao.loadUser("contact@email.com").get().getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.NONE);
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -159,16 +161,22 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
|
||||
@Test
|
||||
void testUpdateRenewalPriceBehavior_setToSpecified() throws Exception {
|
||||
AllocationToken token = persistResource(builderWithPromo().setDiscountFraction(0.5).build());
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "SPECIFIED");
|
||||
assertThat(reloadResource(token).getRenewalPriceBehavior()).isEqualTo(SPECIFIED);
|
||||
AllocationToken token = persistResource(builderWithPromo().build());
|
||||
runCommandForced(
|
||||
"--prefix", "token", "--renewal_price_behavior", "SPECIFIED", "--renewal_price", "USD 1");
|
||||
token = reloadResource(token);
|
||||
assertThat(token.getRenewalPriceBehavior()).isEqualTo(SPECIFIED);
|
||||
assertThat(token.getRenewalPrice()).hasValue(Money.of(CurrencyUnit.USD, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateRenewalPriceBehavior_setToDefault() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
builderWithPromo().setRenewalPriceBehavior(SPECIFIED).setDiscountFraction(0.5).build());
|
||||
builderWithPromo()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 1))
|
||||
.build());
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "default");
|
||||
assertThat(reloadResource(token).getRenewalPriceBehavior()).isEqualTo(DEFAULT);
|
||||
}
|
||||
@@ -177,7 +185,10 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
void testUpdateRenewalPriceBehavior_setToNonPremium() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
builderWithPromo().setRenewalPriceBehavior(SPECIFIED).setDiscountFraction(0.5).build());
|
||||
builderWithPromo()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 1))
|
||||
.build());
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "NONpremium");
|
||||
assertThat(reloadResource(token).getRenewalPriceBehavior()).isEqualTo(NONPREMIUM);
|
||||
}
|
||||
@@ -193,11 +204,26 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
void testUpdateRenewalPriceBehavior_setFromSpecifiedToSpecified() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
builderWithPromo().setRenewalPriceBehavior(SPECIFIED).setDiscountFraction(0.5).build());
|
||||
builderWithPromo()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 1))
|
||||
.build());
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "SPecified");
|
||||
assertThat(reloadResource(token).getRenewalPriceBehavior()).isEqualTo(SPECIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonSpecifiedToSpecified_withoutPrice() throws Exception {
|
||||
persistResource(builderWithPromo().build());
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "SPECIFIED")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateRenewalPriceBehavior_setFromNonPremiumToDefault() throws Exception {
|
||||
AllocationToken token =
|
||||
@@ -214,7 +240,10 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
void testUpdateRenewalPriceBehavior_setToMixedCaseDefault() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
builderWithPromo().setRenewalPriceBehavior(SPECIFIED).setDiscountFraction(0.5).build());
|
||||
builderWithPromo()
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 1))
|
||||
.build());
|
||||
runCommandForced("--prefix", "token", "--renewal_price_behavior", "deFauLt");
|
||||
assertThat(reloadResource(token).getRenewalPriceBehavior()).isEqualTo(DEFAULT);
|
||||
}
|
||||
@@ -350,7 +379,9 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setTokenStatusTransitions(
|
||||
@@ -377,7 +408,9 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setDiscountFraction(1.0)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setRenewalPrice(Money.of(CurrencyUnit.USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setTokenStatusTransitions(
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.tools;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -47,8 +48,9 @@ public class UpdateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
@@ -94,8 +96,9 @@ public class UpdateBulkPricingPackageCommandTest
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, 0))
|
||||
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
|
||||
.setDiscountFraction(1)
|
||||
.setDiscountFraction(1.0)
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
|
||||
+2
-10
@@ -80,7 +80,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
Please click the link below to perform the lock / unlock action on domain example.test. \
|
||||
Note: this code will expire in one hour.
|
||||
|
||||
https://registrarconsole.tld/console-api/registry-lock-verify?lockVerificationCode=\
|
||||
https://registrarconsole.tld/console/#/registry-lock-verify?lockVerificationCode=\
|
||||
123456789ABCDEFGHJKLMNPQRSTUVWXY""";
|
||||
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
@@ -122,15 +122,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
|
||||
@Test
|
||||
void testGet_simpleLock() {
|
||||
saveRegistryLock(
|
||||
new RegistryLock.Builder()
|
||||
.setRepoId("repoId")
|
||||
.setDomainName("example.test")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright 2024 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.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistryLock;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import google.registry.util.StringGenerator;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link ConsoleRegistryLockVerifyAction}. */
|
||||
public class ConsoleRegistryLockVerifyActionTest {
|
||||
|
||||
private static final String DEFAULT_CODE = "123456789ABCDEFGHJKLMNPQRSTUUUUU";
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
|
||||
|
||||
private FakeResponse response;
|
||||
private Domain defaultDomain;
|
||||
private User user;
|
||||
|
||||
private ConsoleRegistryLockVerifyAction action;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("test");
|
||||
defaultDomain = persistActiveDomain("example.test");
|
||||
user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@theregistrar.com")
|
||||
.setRegistryLockEmailAddress("registrylock@theregistrar.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.setRegistryLockPassword("registryLockPassword")
|
||||
.build();
|
||||
action = createAction(DEFAULT_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_lock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_unlock() {
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin_lock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().isSuperuser(true).build());
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(user.getUserRoles().asBuilder().setIsAdmin(true).build())
|
||||
.build();
|
||||
action = createAction(DEFAULT_CODE);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin_unlock() {
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(user.getUserRoles().asBuilder().setIsAdmin(true).build())
|
||||
.build();
|
||||
action = createAction(DEFAULT_CODE);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidCode() {
|
||||
saveRegistryLock(createDefaultLockBuilder().setVerificationCode("foobar").build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Invalid verification code 123456789ABCDEFGHJKLMNPQRSTUUUUU");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_expiredLock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().build());
|
||||
fakeClock.advanceBy(Duration.standardDays(1));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("The pending lock has expired; please try again");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonAdmin_lock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().isSuperuser(true).build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Non-admin user cannot complete admin lock");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonAdmin_unlock() {
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Non-admin user cannot complete admin unlock");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
|
||||
private RegistryLock.Builder createDefaultLockBuilder() {
|
||||
return new RegistryLock.Builder()
|
||||
.setRepoId(defaultDomain.getRepoId())
|
||||
.setDomainName(defaultDomain.getDomainName())
|
||||
.setRegistrarId(defaultDomain.getCurrentSponsorRegistrarId())
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setVerificationCode(DEFAULT_CODE);
|
||||
}
|
||||
|
||||
private ConsoleRegistryLockVerifyAction createAction(String verificationCode) {
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
ConsoleApiParams params = ConsoleApiParamsUtils.createFake(authResult);
|
||||
when(params.request().getMethod()).thenReturn("GET");
|
||||
when(params.request().getServerName()).thenReturn("registrarconsole.tld");
|
||||
DomainLockUtils domainLockUtils =
|
||||
new DomainLockUtils(
|
||||
new DeterministicStringGenerator(StringGenerator.Alphabets.BASE_58),
|
||||
"adminreg",
|
||||
new CloudTasksHelper(fakeClock).getTestCloudTasksUtils());
|
||||
response = (FakeResponse) params.response();
|
||||
return new ConsoleRegistryLockVerifyAction(params, domainLockUtils, GSON, verificationCode);
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -15,11 +15,14 @@
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.OteAccountBuilderTest.verifyIapPermission;
|
||||
import static google.registry.model.OteAccountBuilderTest.verifyUser;
|
||||
import static google.registry.model.registrar.Registrar.loadByRegistrarId;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -35,10 +38,12 @@ import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
@@ -65,6 +70,8 @@ public final class ConsoleOteSetupActionTest {
|
||||
@Order(value = Integer.MAX_VALUE)
|
||||
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
|
||||
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final ConsoleOteSetupAction action = new ConsoleOteSetupAction();
|
||||
private final User user =
|
||||
@@ -100,6 +107,9 @@ public final class ConsoleOteSetupActionTest {
|
||||
|
||||
action.optionalPassword = Optional.empty();
|
||||
action.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
action.maybeGroupEmailAddress = Optional.of("group@example.com");
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
action.iamClient = iamClient;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,9 +152,7 @@ public final class ConsoleOteSetupActionTest {
|
||||
// checking that all the entities are there or that they have the correct values.
|
||||
assertThat(loadByRegistrarId("myclientid-3")).isPresent();
|
||||
assertThat(Tld.get("myclientid-ga")).isNotNull();
|
||||
assertThat(
|
||||
loadByRegistrarId("myclientid-5").get().getContacts().asList().get(0).getEmailAddress())
|
||||
.isEqualTo("contact@registry.example");
|
||||
verifyUser("myclientid-5", "contact@registry.example");
|
||||
assertThat(response.getPayload())
|
||||
.contains("<h1>OT&E successfully created for registrar myclientid!</h1>");
|
||||
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
@@ -163,6 +171,14 @@ public final class ConsoleOteSetupActionTest {
|
||||
Gave user contact@registry.example web access to these Registrars
|
||||
""");
|
||||
assertThat(response.getPayload()).contains("gtag('config', 'sampleId')");
|
||||
verifyIapPermission(
|
||||
"contact@registry.example", action.maybeGroupEmailAddress, cloudTasksHelper, iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPost_authorized_noConsoleGroup() {
|
||||
action.maybeGroupEmailAddress = Optional.empty();
|
||||
testPost_authorized();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+28
-11
@@ -15,10 +15,13 @@
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.OteAccountBuilderTest.verifyIapPermission;
|
||||
import static google.registry.model.OteAccountBuilderTest.verifyUser;
|
||||
import static google.registry.model.registrar.Registrar.loadByRegistrarId;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -29,17 +32,18 @@ import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
@@ -73,6 +77,8 @@ final class ConsoleRegistrarCreatorActionTest {
|
||||
.setEmailAddress("marla.singer@example.com")
|
||||
.setUserRoles(new UserRoles())
|
||||
.build();
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
|
||||
@Mock HttpServletRequest request;
|
||||
@Mock GmailClient gmailClient;
|
||||
@@ -119,6 +125,10 @@ final class ConsoleRegistrarCreatorActionTest {
|
||||
action.passcodeGenerator = new DeterministicStringGenerator("314159265");
|
||||
|
||||
action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId");
|
||||
|
||||
action.maybeGroupEmailAddress = Optional.of("group@example.com");
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
action.iamClient = iamClient;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,8 +162,7 @@ final class ConsoleRegistrarCreatorActionTest {
|
||||
assertThat(response.getPayload()).contains("gtag('config', 'sampleId')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPost_authorized_minimalAddress() {
|
||||
void runTestPost_authorized_minimalAddress() {
|
||||
action.clientId = Optional.of("myclientid");
|
||||
action.name = Optional.of("registrar name");
|
||||
action.billingAccount = Optional.of("USD=billing-account");
|
||||
@@ -209,14 +218,22 @@ final class ConsoleRegistrarCreatorActionTest {
|
||||
.setCountryCode("CC")
|
||||
.build());
|
||||
|
||||
assertThat(registrar.getContacts())
|
||||
.containsExactly(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("myclientid@registry.example")
|
||||
.setEmailAddress("myclientid@registry.example")
|
||||
.setLoginEmailAddress("myclientid@registry.example")
|
||||
.build());
|
||||
verifyUser("myclientid", "myclientid@registry.example");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPost_authorized_minimalAddress_consoleUserGroup() {
|
||||
runTestPost_authorized_minimalAddress();
|
||||
verifyIapPermission(
|
||||
"myclientid@registry.example", action.maybeGroupEmailAddress, cloudTasksHelper, iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPost_authorized_minimalAddress_NoConsoleUserGroup() {
|
||||
action.maybeGroupEmailAddress = Optional.empty();
|
||||
runTestPost_authorized_minimalAddress();
|
||||
verifyIapPermission(
|
||||
"myclientid@registry.example", action.maybeGroupEmailAddress, cloudTasksHelper, iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -41,6 +41,7 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.whois.WhoisResponse.WhoisResponseResults;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -264,7 +265,7 @@ class DomainWhoisResponseTest {
|
||||
StatusValue.CLIENT_RENEW_PROHIBITED,
|
||||
StatusValue.CLIENT_TRANSFER_PROHIBITED,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED))
|
||||
.setRegistrant(registrantResourceKey)
|
||||
.setRegistrant(Optional.of(registrantResourceKey))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(DesignatedContact.Type.ADMIN, adminResourceKey),
|
||||
@@ -291,6 +292,40 @@ class DomainWhoisResponseTest {
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_noRegistrant() {
|
||||
DomainWhoisResponse domainWhoisResponse =
|
||||
new DomainWhoisResponse(
|
||||
domain.asBuilder().setRegistrant(Optional.empty()).build(),
|
||||
false,
|
||||
"Please contact registrar",
|
||||
clock.nowUtc());
|
||||
assertThat(
|
||||
domainWhoisResponse.getResponse(
|
||||
false,
|
||||
"Doodle Disclaimer\nI exist so that carriage return\nin disclaimer can be tested."))
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain_no_registrant.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_noContacts() {
|
||||
DomainWhoisResponse domainWhoisResponse =
|
||||
new DomainWhoisResponse(
|
||||
domain
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build(),
|
||||
false,
|
||||
"Please contact registrar",
|
||||
clock.nowUtc());
|
||||
assertThat(
|
||||
domainWhoisResponse.getResponse(
|
||||
false,
|
||||
"Doodle Disclaimer\nI exist so that carriage return\nin disclaimer can be tested."))
|
||||
.isEqualTo(WhoisResponseResults.create(loadFile("whois_domain_no_contacts.txt"), 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlainTextOutputTest_registrarAbuseInfoMissing() {
|
||||
persistResource(abuseContact.asBuilder().setVisibleInDomainWhoisAsAbuse(false).build());
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:fee12="urn:ietf:params:xml:ns:fee-0.12"
|
||||
>
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:creData>
|
||||
<domain:name>rich.example</domain:name>
|
||||
<domain:crDate>1999-04-03T22:00:00Z</domain:crDate>
|
||||
<domain:exDate>2000-04-03T22:00:00Z</domain:exDate>
|
||||
</domain:creData>
|
||||
</resData>
|
||||
<extension>
|
||||
<fee12:creData>
|
||||
<fee12:currency>USD</fee12:currency>
|
||||
<fee12:fee description="create">13.00</fee12:fee>
|
||||
</fee12:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:infData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:roid>%ROID%</domain:roid>
|
||||
<domain:status s="ok"/>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:host>ns1.example.tld</domain:host>
|
||||
<domain:host>ns2.example.tld</domain:host>
|
||||
<domain:clID>NewRegistrar</domain:clID>
|
||||
<domain:crID>TheRegistrar</domain:crID>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:upID>NewRegistrar</domain:upID>
|
||||
<domain:upDate>1999-12-03T09:00:00.0Z</domain:upDate>
|
||||
<domain:exDate>2005-04-03T22:00:00.0Z</domain:exDate>
|
||||
<domain:trDate>2000-04-08T09:00:00.0Z</domain:trDate>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:infData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<response>
|
||||
<result code="1000">
|
||||
<msg>Command completed successfully</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:infData
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:roid>%ROID%</domain:roid>
|
||||
<domain:status s="ok"/>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.example.tld</domain:hostObj>
|
||||
<domain:hostObj>ns1.example.net</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:host>ns1.example.tld</domain:host>
|
||||
<domain:host>ns2.example.tld</domain:host>
|
||||
<domain:clID>NewRegistrar</domain:clID>
|
||||
<domain:crID>TheRegistrar</domain:crID>
|
||||
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
|
||||
<domain:upID>NewRegistrar</domain:upID>
|
||||
<domain:upDate>1999-12-03T09:00:00.0Z</domain:upDate>
|
||||
<domain:exDate>2005-04-03T22:00:00.0Z</domain:exDate>
|
||||
<domain:trDate>2000-04-08T09:00:00.0Z</domain:trDate>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:infData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
<svTRID>server-trid</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
||||
@@ -1,21 +1,22 @@
|
||||
PATH CLASS METHODS OK MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n APP ADMIN
|
||||
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
|
||||
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
|
||||
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
|
||||
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
|
||||
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
|
||||
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
|
||||
/registrar ConsoleUiAction GET n USER PUBLIC
|
||||
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
|
||||
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
|
||||
/registrar-ote-status OteStatusAction POST n USER PUBLIC
|
||||
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
|
||||
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
|
||||
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
|
||||
PATH CLASS METHODS OK MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n APP ADMIN
|
||||
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
|
||||
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
|
||||
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
|
||||
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
|
||||
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
|
||||
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
|
||||
/registrar ConsoleUiAction GET n USER PUBLIC
|
||||
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
|
||||
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
|
||||
/registrar-ote-status OteStatusAction POST n USER PUBLIC
|
||||
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
|
||||
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
|
||||
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
|
||||
|
||||
@@ -63,6 +63,7 @@ PATH CLASS
|
||||
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
|
||||
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
"ldhName": "%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"status": [
|
||||
"client delete prohibited",
|
||||
"client renew prohibited",
|
||||
"client transfer prohibited",
|
||||
"server update prohibited"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"eventAction": "registration",
|
||||
"eventActor": "TheRegistrar",
|
||||
"eventDate": "1997-01-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "expiration",
|
||||
"eventDate": "2110-10-08T00:44:59.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "last update of RDAP database",
|
||||
"eventDate": "2000-01-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "last changed",
|
||||
"eventDate": "2009-05-29T20:13:00.000Z"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
{
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_1%",
|
||||
"ldhName": "%NAMESERVER_NAME_1%",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/nameserver/%NAMESERVER_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"type": "object truncated due to unexplainable reasons",
|
||||
"description": ["Summary data only. For complete data, send a specific query for the object."]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_2%",
|
||||
"ldhName": "%NAMESERVER_NAME_2%",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/nameserver/%NAMESERVER_NAME_2%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"type": "object truncated due to unexplainable reasons",
|
||||
"description": ["Summary data only. For complete data, send a specific query for the object."]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"secureDNS" : {
|
||||
"delegationSigned": true,
|
||||
"zoneSigned":true,
|
||||
"dsData":[
|
||||
{"algorithm":2,"digest":"DEADFACE","digestType":3,"keyTag":1}
|
||||
]
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "1",
|
||||
"roles" : ["registrar"],
|
||||
"links" : [
|
||||
{
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
{
|
||||
"type" : "IANA Registrar ID",
|
||||
"identifier" : "1"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "%REGISTRAR_FULL_NAME_1%"]
|
||||
]
|
||||
],
|
||||
"entities" : [
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"roles":["abuse"],
|
||||
"status":["active"],
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","Jake Doe"],
|
||||
["tel",{"type":["voice"]},"uri","tel:+1.2125551216"],
|
||||
["tel",{"type":["fax"]},"uri","tel:+1.2125551216"],
|
||||
["email",{},"text","jakedoe@example.com"]
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"description": [
|
||||
"Summary data only. For complete data, send a specific query for the object."
|
||||
],
|
||||
"type": "object truncated due to unexplainable reasons"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles":["registrant"],
|
||||
"vcardArray":[
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "",
|
||||
"roles":["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
]
|
||||
}
|
||||
],
|
||||
"vcardArray":[
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":[
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
],
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization"
|
||||
}
|
||||
],
|
||||
"roles": ["technical"],
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
featureName: "TEST_FEATURE"
|
||||
status:
|
||||
"1970-01-01T00:00:00.000Z": "INACTIVE"
|
||||
"1985-02-15T06:07:08.789Z": "ACTIVE"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user