1
0
mirror of https://github.com/google/nomulus synced 2026-07-06 08:06:33 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Lai Jiang 49ade014ab Remove ofy from Lock (#1771)
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1771)
<!-- Reviewable:end -->
2022-09-07 17:32:03 -04:00
Lai Jiang b8d901effe Remove ofy support from registrar (#1762)
Also fixes some warnings about the use of raw types.
2022-09-07 14:24:42 -04:00
Lai Jiang 23520048dc Remove ofy support from AllocationToken (#1770) 2022-09-07 14:22:42 -04:00
Lai Jiang 37ed6c925c Remove ofy support from RdeRevision (#1772) 2022-09-07 13:30:38 -04:00
Pavlo Tkach 17a21f3326 Update renew flow to accept and process REMOVEPACKAGE token (#1768) 2022-09-02 17:32:59 -04:00
Pavlo Tkach f623da9948 Prohibit renewals of package domains unless REMOVEPACKAGE token is included (#1758) 2022-08-31 18:58:31 -04:00
gbrodman ddc4a615db Fix a few DB issues with the User class (#1766)
- Create a sequence to generate IDs for the user (this allows us to have
  Long ID types so that Hibernate can autogenerate IDs)
- Add an update timestamp column so we can extend BackupGroupRoot
- Add a restriction that there can't be multiple users with the same
  email address
2022-08-31 16:09:07 -04:00
sarahcaseybot 06a1fc0022 Add a packageToken EPP extension for use in the DomainInfo flow (#1760)
* Add a packageToken EPP extension for use in the DomainInfo flow

* small fixes

* Change namespace
2022-08-30 17:50:42 -04:00
48 changed files with 972 additions and 573 deletions
@@ -30,6 +30,7 @@ import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.RegistrarId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.flows.custom.DomainInfoFlowCustomLogic;
@@ -42,6 +43,8 @@ import google.registry.model.domain.DomainCommand.Info.HostsRequest;
import google.registry.model.domain.DomainInfoData;
import google.registry.model.domain.fee06.FeeInfoCommandExtensionV06;
import google.registry.model.domain.fee06.FeeInfoResponseExtensionV06;
import google.registry.model.domain.packagetoken.PackageTokenExtension;
import google.registry.model.domain.packagetoken.PackageTokenResponseExtension;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.rgp.RgpInfoExtension;
import google.registry.model.eppcommon.AuthInfo;
@@ -85,13 +88,14 @@ public final class DomainInfoFlow implements Flow {
@Inject EppResponse.Builder responseBuilder;
@Inject DomainInfoFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic;
@Inject @Superuser boolean isSuperuser;
@Inject
DomainInfoFlow() {}
@Override
public EppResponse run() throws EppException {
extensionManager.register(FeeInfoCommandExtensionV06.class);
extensionManager.register(FeeInfoCommandExtensionV06.class, PackageTokenExtension.class);
flowCustomLogic.beforeValidation();
validateRegistrarIsLoggedIn(registrarId);
extensionManager.validate();
@@ -150,6 +154,15 @@ public final class DomainInfoFlow implements Flow {
if (!gracePeriodStatuses.isEmpty()) {
extensions.add(RgpInfoExtension.create(gracePeriodStatuses));
}
Optional<PackageTokenExtension> packageInfo =
eppInput.getSingleExtension(PackageTokenExtension.class);
if (packageInfo.isPresent()) {
// Package info was requested.
if (isSuperuser || registrarId.equals(domain.getCurrentSponsorRegistrarId())) {
// Only show package info to owning registrar or superusers
extensions.add(PackageTokenResponseExtension.create(domain.getCurrentPackageToken()));
}
}
Optional<FeeInfoCommandExtensionV06> feeInfo =
eppInput.getSingleExtension(FeeInfoCommandExtensionV06.class);
if (feeInfo.isPresent()) { // Fee check was requested.
@@ -30,6 +30,8 @@ import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeApplyPackageRemovalToken;
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@@ -119,6 +121,10 @@ import org.joda.time.Duration;
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.RemovePackageTokenOnNonPackageDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
* @error {@link
* google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException}
@@ -174,7 +180,11 @@ public final class DomainRenewFlow implements TransactionalFlow {
registrarId,
now,
eppInput.getSingleExtension(AllocationTokenExtension.class));
verifyRenewAllowed(authInfo, existingDomain, command);
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
// If client passed an applicable static token this updates the domain
existingDomain = maybeApplyPackageRemovalToken(existingDomain, allocationToken);
int years = command.getPeriod().getValue();
DateTime newExpirationTime =
leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
@@ -302,7 +312,11 @@ public final class DomainRenewFlow implements TransactionalFlow {
.build();
}
private void verifyRenewAllowed(Optional<AuthInfo> authInfo, Domain existingDomain, Renew command)
private void verifyRenewAllowed(
Optional<AuthInfo> authInfo,
Domain existingDomain,
Renew command,
Optional<AllocationToken> allocationToken)
throws EppException {
verifyOptionalAuthInfo(authInfo, existingDomain);
verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES);
@@ -312,6 +326,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
checkHasBillingAccount(registrarId, existingDomain.getTld());
}
verifyUnitIsYears(command.getPeriod());
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
if (!command.getCurrentExpirationDate().equals(
existingDomain.getRegistrationExpirationTime().toLocalDate())) {
@@ -333,7 +349,11 @@ public final class DomainRenewFlow implements TransactionalFlow {
.setPeriodYears(years)
.setCost(renewCost)
.setEventTime(now)
.setAllocationToken(allocationToken.map(AllocationToken::createVKey).orElse(null))
.setAllocationToken(
allocationToken
.filter(t -> AllocationToken.TokenBehavior.DEFAULT.equals(t.getTokenBehavior()))
.map(AllocationToken::createVKey)
.orElse(null))
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
.setDomainHistoryId(
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
@@ -15,6 +15,7 @@
package google.registry.flows.domain.token;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.base.Strings;
@@ -26,9 +27,12 @@ import google.registry.flows.EppException;
import google.registry.flows.EppException.AssociationProhibitsOperationException;
import google.registry.flows.EppException.AuthorizationErrorException;
import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.domain.token.AllocationTokenExtension;
@@ -109,23 +113,27 @@ public class AllocationTokenFlowUtils {
private void validateToken(
InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now)
throws EppException {
if (!token.getAllowedRegistrarIds().isEmpty()
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
throw new AllocationTokenNotValidForRegistrarException();
}
if (!token.getAllowedTlds().isEmpty()
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
throw new AllocationTokenNotValidForTldException();
}
if (token.getDomainName().isPresent()
&& !token.getDomainName().get().equals(domainName.toString())) {
throw new AllocationTokenNotValidForDomainException();
}
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
// check the status transitions map if it's non-trivial.
if (token.getTokenStatusTransitions().size() > 1
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
throw new AllocationTokenNotInPromotionException();
// Only tokens with default behavior require validation
if (TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) {
if (!token.getAllowedRegistrarIds().isEmpty()
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
throw new AllocationTokenNotValidForRegistrarException();
}
if (!token.getAllowedTlds().isEmpty()
&& !token.getAllowedTlds().contains(domainName.parent().toString())) {
throw new AllocationTokenNotValidForTldException();
}
if (token.getDomainName().isPresent()
&& !token.getDomainName().get().equals(domainName.toString())) {
throw new AllocationTokenNotValidForDomainException();
}
// Tokens without status transitions will just have a single-entry NOT_STARTED map, so only
// check the status transitions map if it's non-trivial.
if (token.getTokenStatusTransitions().size() > 1
&& !TokenStatus.VALID.equals(token.getTokenStatusTransitions().getValueAtTime(now))) {
throw new AllocationTokenNotInPromotionException();
}
}
}
@@ -137,8 +145,15 @@ public class AllocationTokenFlowUtils {
// See https://tools.ietf.org/html/draft-ietf-regext-allocation-token-04#section-2.1
throw new InvalidAllocationTokenException();
}
Optional<AllocationToken> maybeTokenEntity =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
Optional<AllocationToken> maybeTokenEntity = AllocationToken.maybeGetStaticTokenInstance(token);
if (maybeTokenEntity.isPresent()) {
return maybeTokenEntity.get();
}
maybeTokenEntity =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.createSql(AllocationToken.class, token)));
if (!maybeTokenEntity.isPresent()) {
throw new InvalidAllocationTokenException();
}
@@ -187,6 +202,49 @@ public class AllocationTokenFlowUtils {
tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now));
}
public static void verifyTokenAllowedOnDomain(
Domain domain, Optional<AllocationToken> allocationToken) throws EppException {
boolean domainHasPackageToken = domain.getCurrentPackageToken().isPresent();
boolean hasRemovePackageToken =
allocationToken.isPresent()
&& TokenBehavior.REMOVE_PACKAGE.equals(allocationToken.get().getTokenBehavior());
if (hasRemovePackageToken && !domainHasPackageToken) {
throw new RemovePackageTokenOnNonPackageDomainException();
} else if (!hasRemovePackageToken && domainHasPackageToken) {
throw new MissingRemovePackageTokenOnPackageDomainException();
}
}
public static Domain maybeApplyPackageRemovalToken(
Domain domain, Optional<AllocationToken> allocationToken) {
if (!allocationToken.isPresent()
|| !TokenBehavior.REMOVE_PACKAGE.equals(allocationToken.get().getTokenBehavior())) {
return domain;
}
Recurring newRecurringBillingEvent =
tm().loadByKey(domain.getAutorenewBillingEvent())
.asBuilder()
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
.setRenewalPrice(null)
.build();
// the Recurring billing event is reloaded later in the renew flow, so we synchronize changed
// RecurringBillingEvent with storage manually
tm().put(newRecurringBillingEvent);
jpaTm().getEntityManager().flush();
jpaTm().getEntityManager().clear();
// Remove current package token
return domain
.asBuilder()
.setCurrentPackageToken(null)
.setAutorenewBillingEvent(newRecurringBillingEvent.createVKey())
.build();
}
// Note: exception messages should be <= 32 characters long for domain check results
/** The allocation token is not currently valid. */
@@ -234,4 +292,20 @@ public class AllocationTokenFlowUtils {
super("The allocation token is invalid");
}
}
/** The __REMOVEPACKAGE__ token is missing on a renew package domain command */
public static class MissingRemovePackageTokenOnPackageDomainException
extends AssociationProhibitsOperationException {
MissingRemovePackageTokenOnPackageDomainException() {
super("Domains that are inside packages cannot be explicitly renewed");
}
}
/** The __REMOVEPACKAGE__ token is not allowed on non package domains */
public static class RemovePackageTokenOnNonPackageDomainException
extends AssociationProhibitsOperationException {
RemovePackageTokenOnNonPackageDomainException() {
super("__REMOVEPACKAGE__ token is not allowed on non package domains");
}
}
}
@@ -22,16 +22,12 @@ import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.server.Lock;
import google.registry.model.server.ServerSecret;
/** Sets of classes of the Objectify-registered entities in use throughout the model. */
@@ -41,7 +37,6 @@ public final class EntityClasses {
/** Set of entity classes. */
public static final ImmutableSet<Class<? extends ImmutableObject>> ALL_CLASSES =
ImmutableSet.of(
AllocationToken.class,
Contact.class,
ContactHistory.class,
Domain.class,
@@ -56,9 +51,6 @@ public final class EntityClasses {
HistoryEntry.class,
Host.class,
HostHistory.class,
Lock.class,
RdeRevision.class,
Registrar.class,
ServerSecret.class);
private EntityClasses() {}
@@ -257,7 +257,7 @@ public abstract class ImmutableObject implements Cloneable {
return (Map<String, Object>) toMapRecursive(this);
}
public VKey createVKey() {
public VKey<? extends ImmutableObject> createVKey() {
throw new UnsupportedOperationException("VKey creation is not supported for this entity");
}
}
@@ -212,6 +212,9 @@ public abstract class BillingEvent extends ImmutableObject
return nullToEmptyImmutableCopy(flags);
}
@Override
public abstract VKey<? extends BillingEvent> createVKey();
/** Override Buildable.asBuilder() to give this method stronger typing. */
@Override
public abstract Builder<?, ?> asBuilder();
@@ -277,7 +277,7 @@ public class DomainBase extends EppResource
@Ignore DateTime dnsRefreshRequestTime;
/** The {@link AllocationToken} for the package this domain is currently a part of. */
@Nullable VKey<AllocationToken> currentPackageToken;
@Ignore @Nullable VKey<AllocationToken> currentPackageToken;
/**
* Returns the DNS refresh request time iff this domain's DNS needs refreshing, otherwise absent.
@@ -0,0 +1,23 @@
// Copyright 2022 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.model.domain.packagetoken;
import google.registry.model.ImmutableObject;
import google.registry.model.eppinput.EppInput.CommandExtension;
import javax.xml.bind.annotation.XmlRootElement;
/** A package token extension that may be present on EPP domain commands. */
@XmlRootElement(name = "info")
public class PackageTokenExtension extends ImmutableObject implements CommandExtension {}
@@ -0,0 +1,45 @@
// Copyright 2022 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.model.domain.packagetoken;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.persistence.VKey;
import google.registry.xml.TrimWhitespaceAdapter;
import java.util.Optional;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
* An XML data object that represents a package token extension that may be present on the response
* to EPP domain info commands.
*/
@XmlRootElement(name = "packageData")
public class PackageTokenResponseExtension extends ImmutableObject implements ResponseExtension {
/** Token string of the PACKAGE token the name belongs to. */
@XmlJavaTypeAdapter(TrimWhitespaceAdapter.class)
String token;
public static PackageTokenResponseExtension create(Optional<VKey<AllocationToken>> tokenKey) {
PackageTokenResponseExtension instance = new PackageTokenResponseExtension();
instance.token = "";
if (tokenKey.isPresent()) {
instance.token = tokenKey.get().getSqlKey().toString();
}
return instance;
}
}
@@ -0,0 +1,27 @@
// Copyright 2022 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.
@XmlSchema(
namespace = "urn:google:params:xml:ns:packageToken-1.0",
xmlns =
@XmlNs(prefix = "packageToken", namespaceURI = "urn:google:params:xml:ns:packageToken-1.0"),
elementFormDefault = XmlNsForm.QUALIFIED)
@XmlAccessorType(XmlAccessType.FIELD)
package google.registry.model.domain.packagetoken;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
@@ -26,22 +26,16 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Range;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils;
import google.registry.model.BackupGroupRoot;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.reporting.HistoryEntry;
@@ -54,31 +48,31 @@ import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import org.joda.time.DateTime;
/** An entity representing an allocation token. */
@ReportedOn
@Entity
@WithStringVKey
@javax.persistence.Entity
@WithStringVKey(compositeKey = true)
@Table(
indexes = {
@javax.persistence.Index(
columnList = "token",
name = "allocation_token_token_idx",
unique = true),
@javax.persistence.Index(
columnList = "domainName",
name = "allocation_token_domain_name_idx"),
@javax.persistence.Index(columnList = "tokenType"),
@javax.persistence.Index(columnList = "redemption_domain_repo_id")
@Index(columnList = "token", name = "allocation_token_token_idx", unique = true),
@Index(columnList = "domainName", name = "allocation_token_domain_name_idx"),
@Index(columnList = "tokenType"),
@Index(columnList = "redemption_domain_repo_id")
})
public class AllocationToken extends BackupGroupRoot implements Buildable {
private static final long serialVersionUID = -3954475393220876903L;
private static final String REMOVE_PACKAGE = "__REMOVEPACKAGE__";
private static final ImmutableMap<String, TokenBehavior> STATIC_TOKEN_BEHAVIORS =
ImmutableMap.of(REMOVE_PACKAGE, TokenBehavior.REMOVE_PACKAGE);
// Promotions should only move forward, and ENDED / CANCELLED are terminal states.
private static final ImmutableMultimap<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS =
@@ -87,6 +81,18 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
.putAll(VALID, ENDED, CANCELLED)
.build();
private static final ImmutableMap<String, AllocationToken> BEHAVIORAL_TOKENS =
ImmutableMap.of(
REMOVE_PACKAGE,
new AllocationToken.Builder()
.setTokenType(TokenType.UNLIMITED_USE)
.setToken(REMOVE_PACKAGE)
.build());
public static Optional<AllocationToken> maybeGetStaticTokenInstance(String name) {
return Optional.ofNullable(BEHAVIORAL_TOKENS.get(name));
}
/** Any special behavior that should be used when registering domains using this token. */
public enum RegistrationBehavior {
/** No special behavior */
@@ -110,7 +116,21 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
public enum TokenType {
PACKAGE,
SINGLE_USE,
UNLIMITED_USE
UNLIMITED_USE,
}
/**
* System behaves differently based on a token it gets inside a command. This enumerates different
* types of behaviors we support.
*/
public enum TokenBehavior {
/** No special behavior */
DEFAULT,
/**
* REMOVE_PACKAGE triggers domain removal from promotional package, bypasses DEFAULT token
* validations.
*/
REMOVE_PACKAGE
}
/** The status of this token with regard to any potential promotion. */
@@ -126,11 +146,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
}
/** The allocation token string. */
@javax.persistence.Id @Id String token;
@Id String token;
/** The key of the history entry for which the token was used. Null if not yet used. */
@Nullable
@Index
@AttributeOverrides({
@AttributeOverride(name = "repoId", column = @Column(name = "redemption_domain_repo_id")),
@AttributeOverride(
@@ -140,10 +159,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
DomainHistoryVKey redemptionHistoryEntry;
/** The fully-qualified domain name that this token is limited to, if any. */
@Nullable @Index String domainName;
@Nullable String domainName;
/** When this token was created. */
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds")
@@ -172,21 +191,12 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
@Enumerated(EnumType.STRING)
@Column(name = "renewalPriceBehavior", nullable = false)
@Ignore
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
@OnLoad
void onLoad() {
if (discountYears == 0) {
discountYears = 1;
}
}
/**
* Promotional token validity periods.
*
@@ -255,9 +265,17 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return registrationBehavior;
}
public TokenBehavior getTokenBehavior() {
return STATIC_TOKEN_BEHAVIORS.getOrDefault(token, TokenBehavior.DEFAULT);
}
@Override
public VKey<AllocationToken> createVKey() {
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
if (!AllocationToken.TokenBehavior.DEFAULT.equals(getTokenBehavior())) {
throw new IllegalArgumentException(
String.format("%s tokens are not stored in the database", getTokenBehavior()));
}
return VKey.createSql(AllocationToken.class, getToken());
}
@Override
@@ -291,7 +309,8 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
"Redemption history entry can only be specified for SINGLE_USE tokens");
checkArgument(
getInstance().tokenType != TokenType.PACKAGE
|| getInstance().allowedClientIds.size() == 1,
|| (getInstance().allowedClientIds != null
&& getInstance().allowedClientIds.size() == 1),
"PACKAGE tokens must have exactly one allowed client registrar");
checkArgument(
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
@@ -31,25 +31,27 @@ import java.io.ByteArrayOutputStream;
public class EppXmlTransformer {
// Hardcoded XML schemas, ordered with respect to dependency.
private static final ImmutableList<String> SCHEMAS = ImmutableList.of(
"eppcom.xsd",
"epp.xsd",
"contact.xsd",
"host.xsd",
"domain.xsd",
"rgp.xsd",
"secdns.xsd",
"fee06.xsd",
"fee11.xsd",
"fee12.xsd",
"metadata.xsd",
"mark.xsd",
"dsig.xsd",
"smd.xsd",
"launch.xsd",
"allocate.xsd",
"superuser.xsd",
"allocationToken-1.0.xsd");
private static final ImmutableList<String> SCHEMAS =
ImmutableList.of(
"eppcom.xsd",
"epp.xsd",
"contact.xsd",
"host.xsd",
"domain.xsd",
"rgp.xsd",
"secdns.xsd",
"fee06.xsd",
"fee11.xsd",
"fee12.xsd",
"metadata.xsd",
"mark.xsd",
"dsig.xsd",
"smd.xsd",
"launch.xsd",
"allocate.xsd",
"superuser.xsd",
"allocationToken-1.0.xsd",
"packageToken.xsd");
private static final XmlTransformer INPUT_TRANSFORMER =
new XmlTransformer(SCHEMAS, EppInput.class);
@@ -45,6 +45,7 @@ import google.registry.model.domain.launch.LaunchDeleteExtension;
import google.registry.model.domain.launch.LaunchInfoExtension;
import google.registry.model.domain.launch.LaunchUpdateExtension;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.packagetoken.PackageTokenExtension;
import google.registry.model.domain.rgp.RgpUpdateExtension;
import google.registry.model.domain.secdns.SecDnsCreateExtension;
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
@@ -362,6 +363,7 @@ public class EppInput extends ImmutableObject {
// Other extensions
@XmlElementRef(type = AllocationTokenExtension.class),
@XmlElementRef(type = MetadataExtension.class),
@XmlElementRef(type = PackageTokenExtension.class),
@XmlElementRef(type = RgpUpdateExtension.class),
@XmlElementRef(type = SecDnsCreateExtension.class),
@XmlElementRef(type = SecDnsUpdateExtension.class)
@@ -43,6 +43,7 @@ import google.registry.model.domain.fee12.FeeRenewResponseExtensionV12;
import google.registry.model.domain.fee12.FeeTransferResponseExtensionV12;
import google.registry.model.domain.fee12.FeeUpdateResponseExtensionV12;
import google.registry.model.domain.launch.LaunchCheckResponseExtension;
import google.registry.model.domain.packagetoken.PackageTokenResponseExtension;
import google.registry.model.domain.rgp.RgpInfoExtension;
import google.registry.model.domain.secdns.SecDnsInfoExtension;
import google.registry.model.eppcommon.Trid;
@@ -121,28 +122,30 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting {
/** Zero or more response extensions. */
@XmlElementRefs({
@XmlElementRef(type = FeeCheckResponseExtensionV06.class),
@XmlElementRef(type = FeeInfoResponseExtensionV06.class),
@XmlElementRef(type = FeeCreateResponseExtensionV06.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV06.class),
@XmlElementRef(type = FeeRenewResponseExtensionV06.class),
@XmlElementRef(type = FeeTransferResponseExtensionV06.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV06.class),
@XmlElementRef(type = FeeCheckResponseExtensionV11.class),
@XmlElementRef(type = FeeCreateResponseExtensionV11.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV11.class),
@XmlElementRef(type = FeeRenewResponseExtensionV11.class),
@XmlElementRef(type = FeeTransferResponseExtensionV11.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV11.class),
@XmlElementRef(type = FeeCheckResponseExtensionV12.class),
@XmlElementRef(type = FeeCreateResponseExtensionV12.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV12.class),
@XmlElementRef(type = FeeRenewResponseExtensionV12.class),
@XmlElementRef(type = FeeTransferResponseExtensionV12.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV12.class),
@XmlElementRef(type = LaunchCheckResponseExtension.class),
@XmlElementRef(type = RgpInfoExtension.class),
@XmlElementRef(type = SecDnsInfoExtension.class) })
@XmlElementRef(type = FeeCheckResponseExtensionV06.class),
@XmlElementRef(type = FeeInfoResponseExtensionV06.class),
@XmlElementRef(type = FeeCreateResponseExtensionV06.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV06.class),
@XmlElementRef(type = FeeRenewResponseExtensionV06.class),
@XmlElementRef(type = FeeTransferResponseExtensionV06.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV06.class),
@XmlElementRef(type = FeeCheckResponseExtensionV11.class),
@XmlElementRef(type = FeeCreateResponseExtensionV11.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV11.class),
@XmlElementRef(type = FeeRenewResponseExtensionV11.class),
@XmlElementRef(type = FeeTransferResponseExtensionV11.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV11.class),
@XmlElementRef(type = FeeCheckResponseExtensionV12.class),
@XmlElementRef(type = FeeCreateResponseExtensionV12.class),
@XmlElementRef(type = FeeDeleteResponseExtensionV12.class),
@XmlElementRef(type = FeeRenewResponseExtensionV12.class),
@XmlElementRef(type = FeeTransferResponseExtensionV12.class),
@XmlElementRef(type = FeeUpdateResponseExtensionV12.class),
@XmlElementRef(type = LaunchCheckResponseExtension.class),
@XmlElementRef(type = PackageTokenResponseExtension.class),
@XmlElementRef(type = RgpInfoExtension.class),
@XmlElementRef(type = SecDnsInfoExtension.class)
})
@XmlElementWrapper(name = "extension")
ImmutableList<? extends ResponseExtension> extensions;
@@ -215,6 +215,9 @@ public abstract class PollMessage extends ImmutableObject
return domainRepoId != null ? Type.DOMAIN : contactRepoId != null ? Type.CONTACT : Type.HOST;
}
@Override
public abstract VKey<? extends PollMessage> createVKey();
public abstract ImmutableList<ResponseData> getResponseData();
/** Override Buildable.asBuilder() to give this method stronger typing. */
@@ -19,10 +19,6 @@ import static google.registry.model.rde.RdeNamingUtils.makePartialName;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.base.VerifyException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.BackupGroupRoot;
import google.registry.model.ImmutableObject;
import google.registry.model.rde.RdeRevision.RdeRevisionId;
@@ -32,10 +28,11 @@ import java.io.Serializable;
import java.util.Optional;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Transient;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
@@ -47,18 +44,14 @@ import org.joda.time.LocalDate;
* flag is included in the generated XML.
*/
@Entity
@javax.persistence.Entity
@IdClass(RdeRevisionId.class)
public final class RdeRevision extends BackupGroupRoot {
/** String triplet of tld, date, and mode, e.g. {@code soy_2015-09-01_full}. */
@Id @Transient String id;
@Id String tld;
@javax.persistence.Id @Ignore String tld;
@Id LocalDate date;
@javax.persistence.Id @Ignore LocalDate date;
@javax.persistence.Id @Ignore RdeMode mode;
@Id RdeMode mode;
/**
* Number of last revision successfully staged to GCS.
@@ -71,10 +64,8 @@ public final class RdeRevision extends BackupGroupRoot {
/** Hibernate requires an empty constructor. */
private RdeRevision() {}
public static RdeRevision create(
String id, String tld, LocalDate date, RdeMode mode, int revision) {
public static RdeRevision create(String tld, LocalDate date, RdeMode mode, int revision) {
RdeRevision instance = new RdeRevision();
instance.id = id;
instance.tld = tld;
instance.date = date;
instance.mode = mode;
@@ -92,12 +83,9 @@ public final class RdeRevision extends BackupGroupRoot {
* @return {@code 0} for first deposit generation and {@code >0} for resends
*/
public static int getNextRevision(String tld, DateTime date, RdeMode mode) {
String id = makePartialName(tld, date, mode);
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, id);
Optional<RdeRevision> revisionOptional =
tm().transact(
() -> tm().loadByKeyIfPresent(VKey.create(RdeRevision.class, sqlKey, ofyKey)));
tm().transact(() -> tm().loadByKeyIfPresent(VKey.createSql(RdeRevision.class, sqlKey)));
return revisionOptional.map(rdeRevision -> rdeRevision.revision + 1).orElse(0);
}
@@ -121,12 +109,10 @@ public final class RdeRevision extends BackupGroupRoot {
*/
public static void saveRevision(String tld, DateTime date, RdeMode mode, int revision) {
checkArgument(revision >= 0, "Negative revision: %s", revision);
String triplet = makePartialName(tld, date, mode);
tm().assertInTransaction();
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, triplet);
Optional<RdeRevision> revisionOptional =
tm().loadByKeyIfPresent(VKey.create(RdeRevision.class, sqlKey, ofyKey));
tm().loadByKeyIfPresent(VKey.createSql(RdeRevision.class, sqlKey));
if (revision == 0) {
revisionOptional.ifPresent(
rdeRevision -> {
@@ -139,7 +125,7 @@ public final class RdeRevision extends BackupGroupRoot {
checkArgument(
revisionOptional.isPresent(),
"Couldn't find existing RDE revision %s when trying to save new revision %s",
triplet,
makePartialName(tld, date, mode),
revision);
checkArgument(
revisionOptional.get().revision == revision - 1,
@@ -147,7 +133,7 @@ public final class RdeRevision extends BackupGroupRoot {
revision - 1,
revisionOptional.get());
}
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
RdeRevision object = create(tld, date.toLocalDate(), mode, revision);
tm().put(object);
}
@@ -26,8 +26,6 @@ import static com.google.common.collect.Sets.immutableEnumSet;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer;
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.model.tld.Registries.assertTldsExist;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@@ -44,7 +42,6 @@ import static java.util.function.Predicate.isEqual;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -55,12 +52,6 @@ import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
@@ -68,9 +59,6 @@ import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import google.registry.model.UnsafeSerializable;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldType;
import google.registry.persistence.VKey;
@@ -83,6 +71,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
@@ -90,25 +79,22 @@ import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.joda.money.CurrencyUnit;
import org.joda.time.DateTime;
/** Information about a registrar. */
@ReportedOn
@Entity
@javax.persistence.Entity
@Table(
indexes = {
@javax.persistence.Index(columnList = "registrarName", name = "registrar_name_idx"),
@javax.persistence.Index(
columnList = "ianaIdentifier",
name = "registrar_iana_identifier_idx"),
@Index(columnList = "registrarName", name = "registrar_name_idx"),
@Index(columnList = "ianaIdentifier", name = "registrar_iana_identifier_idx"),
})
@InCrossTld
public class Registrar extends ImmutableObject
implements Buildable, Jsonifiable, UnsafeSerializable {
@@ -197,7 +183,7 @@ public class Registrar extends ImmutableObject
/** The states in which a {@link Registrar} is considered {@link #isLive live}. */
private static final ImmutableSet<State> LIVE_STATES =
Sets.immutableEnumSet(State.ACTIVE, State.SUSPENDED);
immutableEnumSet(State.ACTIVE, State.SUSPENDED);
/**
* The types for which a {@link Registrar} should be included in WHOIS and RDAP output. We exclude
@@ -213,18 +199,9 @@ public class Registrar extends ImmutableObject
private static final Comparator<RegistrarPoc> CONTACT_EMAIL_COMPARATOR =
comparing(RegistrarPoc::getEmailAddress, String::compareTo);
/**
* A caching {@link Supplier} of a registrarId to {@link Registrar} map.
*
* <p>The supplier's get() method enters a transactionless context briefly to avoid enrolling the
* query inside an unrelated client-affecting transaction.
*/
/** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */
private static final Supplier<ImmutableMap<String, Registrar>> CACHE_BY_REGISTRAR_ID =
memoizeWithShortExpiration(
() ->
tm().doTransactionless(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId)));
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
memoizeWithShortExpiration(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId));
/**
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
@@ -233,7 +210,6 @@ public class Registrar extends ImmutableObject
* <p>TODO(b/177567432): Rename this field to registrarId.
*/
@Id
@javax.persistence.Id
@Column(name = "registrarId", nullable = false)
String clientIdentifier;
@@ -248,7 +224,6 @@ public class Registrar extends ImmutableObject
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
* Registrars</a>
*/
@Index
@Column(nullable = false)
String registrarName;
@@ -313,7 +288,6 @@ public class Registrar extends ImmutableObject
* Localized {@link RegistrarAddress} for this registrar. Contents can be represented in
* unrestricted UTF-8.
*/
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(
@@ -338,7 +312,6 @@ public class Registrar extends ImmutableObject
* Internationalized {@link RegistrarAddress} for this registrar. All contained values must be
* representable in the 7-bit US-ASCII character set.
*/
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")),
@@ -367,14 +340,14 @@ public class Registrar extends ImmutableObject
*
* <ul>
* <li>8 is used for Testing Registrar.
* <li>9997 is used by ICAAN for SLA monitoring.
* <li>9997 is used by ICANN for SLA monitoring.
* <li>9999 is used for cases when the registry operator acts as registrar.
* </ul>
*
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
* IDs</a>
*/
@Index @Nullable Long ianaIdentifier;
@Nullable Long ianaIdentifier;
/** Purchase Order number used for invoices in external billing system, if applicable. */
@Nullable String poNumber;
@@ -395,7 +368,7 @@ public class Registrar extends ImmutableObject
/**
* ICANN referral email address.
*
* <p>This value is specified in the initial registrar contact. It can't be edited in the web GUI
* <p>This value is specified in the initial registrar contact. It can't be edited in the web GUI,
* and it must be specified when the registrar account is created.
*/
String icannReferralEmail;
@@ -406,10 +379,10 @@ public class Registrar extends ImmutableObject
// Metadata.
/** The time when this registrar was created. */
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** An automatically managed last-saved timestamp. */
@Ignore UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;
@@ -610,18 +583,13 @@ public class Registrar extends ImmutableObject
}
private Iterable<RegistrarPoc> getContactsIterable() {
if (tm().isOfy()) {
return auditedOfy().load().type(RegistrarPoc.class).ancestor(Registrar.this);
} else {
return tm().transact(
() ->
jpaTm()
.query(
"FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
.setParameter("registrarId", clientIdentifier)
.getResultStream()
.collect(toImmutableList()));
}
return tm().transact(
() ->
jpaTm()
.query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
.setParameter("registrarId", clientIdentifier)
.getResultStream()
.collect(toImmutableList()));
}
@Override
@@ -687,18 +655,13 @@ public class Registrar extends ImmutableObject
/** Creates a {@link VKey} for this instance. */
@Override
public VKey<Registrar> createVKey() {
return createVKey(Key.create(this));
return createVKey(clientIdentifier);
}
/** Creates a {@link VKey} for the given {@code registrarId}. */
public static VKey<Registrar> createVKey(String registrarId) {
checkArgumentNotNull(registrarId, "registrarId must be specified");
return createVKey(Key.create(getCrossTldKey(), Registrar.class, registrarId));
}
/** Creates a {@link VKey} instance from a {@link Key} instance. */
public static VKey<Registrar> createVKey(Key<Registrar> key) {
return VKey.create(Registrar.class, key.getName(), key);
return VKey.createSql(Registrar.class, registrarId);
}
/** A builder for constructing {@link Registrar}, since it is immutable. */
@@ -774,7 +737,7 @@ public class Registrar extends ImmutableObject
Set<VKey<Registry>> missingTldKeys =
Sets.difference(
newTldKeys, tm().transact(() -> tm().loadByKeysIfPresent(newTldKeys)).keySet());
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexisting TLDs: %s", missingTldKeys);
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds);
return this;
}
@@ -16,7 +16,6 @@ package google.registry.model.server;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@@ -24,15 +23,8 @@ import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManager;
import google.registry.util.RequestStatusChecker;
import google.registry.util.RequestStatusCheckerImpl;
import java.io.Serializable;
@@ -40,6 +32,8 @@ import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
import javax.persistence.Table;
@@ -57,8 +51,6 @@ import org.joda.time.Duration;
* safe calls that automatically lock and unlock, see LockHandler.
*/
@Entity
@NotBackedUp(reason = Reason.TRANSIENT)
@javax.persistence.Entity
@Table
@IdClass(Lock.LockId.class)
public class Lock extends ImmutableObject implements Serializable {
@@ -109,14 +101,13 @@ public class Lock extends ImmutableObject implements Serializable {
/** The resource name used to create the lock. */
@Column(nullable = false)
@javax.persistence.Id
@Id
String resourceName;
/** The tld used to create the lock, or GLOBAL if it's cross-TLD. */
// TODO(b/177567432): rename to "scope" post-Datastore
@Column(nullable = false, name = "scope")
@javax.persistence.Id
String tld;
@Column(nullable = false)
@Id
String scope;
public DateTime getExpirationTime() {
return expirationTime;
@@ -124,7 +115,7 @@ public class Lock extends ImmutableObject implements Serializable {
@PostLoad
void postLoad() {
lockId = makeLockId(resourceName, tld);
lockId = makeLockId(resourceName, scope);
}
/**
@@ -146,7 +137,7 @@ public class Lock extends ImmutableObject implements Serializable {
instance.expirationTime = acquiredTime.plus(leaseLength);
instance.acquiredTime = acquiredTime;
instance.resourceName = resourceName;
instance.tld = scope;
instance.scope = scope;
return instance;
}
@@ -157,8 +148,13 @@ public class Lock extends ImmutableObject implements Serializable {
@AutoValue
abstract static class AcquireResult {
public abstract DateTime transactionTime();
public abstract @Nullable Lock existingLock();
public abstract @Nullable Lock newLock();
@Nullable
public abstract Lock existingLock();
@Nullable
public abstract Lock newLock();
public abstract LockState lockState();
public static AcquireResult create(
@@ -213,64 +209,18 @@ public class Lock extends ImmutableObject implements Serializable {
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning) {
return acquireWithTransactionManager(
resourceName, tld, leaseLength, requestStatusChecker, checkThreadRunning, tm());
}
/**
* Try to acquire a lock in SQL. Returns absent if it can't be acquired.
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*/
public static Optional<Lock> acquireSql(
String resourceName,
@Nullable String tld,
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning) {
return acquireWithTransactionManager(
resourceName, tld, leaseLength, requestStatusChecker, checkThreadRunning, jpaTm());
}
/** Release the lock. */
public void release() {
releaseWithTransactionManager(tm());
}
/**
* Release the lock from SQL.
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*/
public void releaseSql() {
releaseWithTransactionManager(jpaTm());
}
/** Try to acquire a lock. Returns absent if it can't be acquired. */
private static Optional<Lock> acquireWithTransactionManager(
String resourceName,
@Nullable String tld,
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning,
TransactionManager transactionManager) {
String scope = (tld != null) ? tld : GLOBAL;
String lockId = makeLockId(resourceName, scope);
String scope = tld != null ? tld : GLOBAL;
// It's important to use transactNew rather than transact, because a Lock can be used to control
// access to resources like GCS that can't be transactionally rolled back. Therefore, the lock
// must be definitively acquired before it is used, even when called inside another transaction.
Supplier<AcquireResult> lockAcquirer =
() -> {
DateTime now = transactionManager.getTransactionTime();
DateTime now = jpaTm().getTransactionTime();
// Checking if an unexpired lock still exists - if so, the lock can't be acquired.
Lock lock =
transactionManager
.loadByKeyIfPresent(
VKey.create(
Lock.class,
new LockId(resourceName, scope),
Key.create(Lock.class, lockId)))
jpaTm()
.loadByKeyIfPresent(VKey.createSql(Lock.class, new LockId(resourceName, scope)))
.orElse(null);
if (lock != null) {
logger.atInfo().log(
@@ -292,15 +242,11 @@ public class Lock extends ImmutableObject implements Serializable {
create(resourceName, scope, requestStatusChecker.getLogId(), now, leaseLength);
// Locks are not parented under an EntityGroupRoot (so as to avoid write
// contention) and don't need to be backed up.
transactionManager.put(newLock);
jpaTm().put(newLock);
return AcquireResult.create(now, lock, newLock, lockState);
};
// In ofy, backup is determined per-action, but in SQL it's determined per-transaction
AcquireResult acquireResult =
transactionManager.isOfy()
? transactionManager.transactNew(lockAcquirer)
: ((JpaTransactionManager) transactionManager).transactWithoutBackup(lockAcquirer);
AcquireResult acquireResult = jpaTm().transactWithoutBackup(lockAcquirer);
logAcquireResult(acquireResult);
lockMetrics.recordAcquire(resourceName, scope, acquireResult.lockState());
@@ -308,62 +254,51 @@ public class Lock extends ImmutableObject implements Serializable {
}
/** Release the lock. */
private void releaseWithTransactionManager(TransactionManager transactionManager) {
public void release() {
// Just use the default clock because we aren't actually doing anything that will use the clock.
Supplier<Void> lockReleaser =
() -> {
// To release a lock, check that no one else has already obtained it and if not
// delete it. If the lock in Datastore was different then this lock is gone already;
// delete it. If the lock in the database was different, then this lock is gone already;
// this can happen if release() is called around the expiration time and the lock
// expires underneath us.
VKey<Lock> key =
VKey.create(
Lock.class, new LockId(resourceName, tld), Key.create(Lock.class, lockId));
Lock loadedLock = transactionManager.loadByKeyIfPresent(key).orElse(null);
if (Lock.this.equals(loadedLock)) {
VKey<Lock> key = VKey.createSql(Lock.class, new LockId(resourceName, scope));
Lock loadedLock = jpaTm().loadByKeyIfPresent(key).orElse(null);
if (equals(loadedLock)) {
// Use deleteIgnoringReadOnly() so that we don't create a commit log entry for deleting
// the lock.
logger.atInfo().log("Deleting lock: %s", lockId);
transactionManager.delete(key);
jpaTm().delete(key);
lockMetrics.recordRelease(
resourceName,
tld,
new Duration(acquiredTime, transactionManager.getTransactionTime()));
resourceName, scope, new Duration(acquiredTime, jpaTm().getTransactionTime()));
} else {
logger.atSevere().log(
"The lock we acquired was transferred to someone else before we"
+ " released it! Did action take longer than lease length?"
+ " Our lock: %s, current lock: %s",
Lock.this, loadedLock);
this, loadedLock);
logger.atInfo().log(
"Not deleting lock: %s - someone else has it: %s", lockId, loadedLock);
}
return null;
};
// In ofy, backup is determined per-action, but in SQL it's determined per-transaction
if (transactionManager.isOfy()) {
transactionManager.transact(lockReleaser);
} else {
((JpaTransactionManager) transactionManager).transactWithoutBackup(lockReleaser);
}
jpaTm().transactWithoutBackup(lockReleaser);
}
static class LockId extends ImmutableObject implements Serializable {
String resourceName;
// TODO(b/177567432): rename to "scope" post-Datastore
@Column(name = "scope")
String tld;
String scope;
// Required for Hibernate
@SuppressWarnings("unused")
private LockId() {}
LockId(String resourceName, String tld) {
LockId(String resourceName, String scope) {
this.resourceName = checkArgumentNotNull(resourceName, "The resource name cannot be null");
this.tld = checkArgumentNotNull(tld, "Scope of a lock cannot be null");
this.scope = checkArgumentNotNull(scope, "Scope of a lock cannot be null");
}
}
}
@@ -38,8 +38,8 @@ import javax.persistence.MappedSuperclass;
*/
@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class EppHistoryVKey<K, E extends EppResource> extends ImmutableObject
implements Serializable {
public abstract class EppHistoryVKey<K extends ImmutableObject, E extends EppResource>
extends ImmutableObject implements Serializable {
private static final long serialVersionUID = -3906580677709539818L;
@@ -38,26 +38,5 @@ public interface LockHandler extends Serializable {
* @return true if all locks were acquired and the callable was run; false otherwise.
*/
boolean executeWithLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames);
/**
* Acquire one or more locks using only Cloud SQL and execute a Void {@link Callable}.
*
* <p>Runs on a thread that will be killed if it doesn't complete before the lease expires.
*
* <p>Note that locks are specific either to a given tld or to the entire system (in which case
* tld should be passed as null).
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*
* @return true if all locks were acquired and the callable was run; false otherwise.
*/
boolean executeWithSqlLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames);
Callable<Void> callable, @Nullable String tld, Duration leaseLength, String... lockNames);
}
@@ -76,27 +76,6 @@ public class LockHandlerImpl implements LockHandler {
return executeWithLockAcquirer(callable, tld, leaseLength, this::acquire, lockNames);
}
/**
* Acquire one or more locks using only Cloud SQL and execute a Void {@link Callable}.
*
* <p>Thread will be killed if it doesn't complete before the lease expires.
*
* <p>Note that locks are specific either to a given tld or to the entire system (in which case
* tld should be passed as null).
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*
* @return whether all locks were acquired and the callable was run.
*/
@Override
public boolean executeWithSqlLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames) {
return executeWithLockAcquirer(callable, tld, leaseLength, this::acquireSql, lockNames);
}
private boolean executeWithLockAcquirer(
final Callable<Void> callable,
@Nullable String tld,
@@ -138,11 +117,6 @@ public class LockHandlerImpl implements LockHandler {
return Lock.acquire(lockName, tld, leaseLength, requestStatusChecker, true);
}
@VisibleForTesting
Optional<Lock> acquireSql(String lockName, @Nullable String tld, Duration leaseLength) {
return Lock.acquireSql(lockName, tld, leaseLength, requestStatusChecker, true);
}
private interface LockAcquirer {
Optional<Lock> acquireLock(String lockName, @Nullable String tld, Duration leaseLength);
}
@@ -314,7 +314,7 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
private ImmutableSet<String> getExistingTokenStrings(ImmutableSet<String> candidates) {
ImmutableSet<VKey<AllocationToken>> existingTokenKeys =
candidates.stream()
.map(input -> VKey.create(AllocationToken.class, input))
.map(input -> VKey.createSql(AllocationToken.class, input))
.collect(toImmutableSet());
return tm().transact(
() ->
@@ -47,7 +47,7 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
for (List<String> tokens : Lists.partition(mainParameters, BATCH_SIZE)) {
ImmutableList<VKey<AllocationToken>> tokenKeys =
tokens.stream()
.map(t -> VKey.create(AllocationToken.class, t))
.map(t -> VKey.createSql(AllocationToken.class, t))
.collect(toImmutableList());
tm().transact(
() ->
@@ -55,7 +55,7 @@ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand
if (tokens != null) {
ImmutableSet<VKey<AllocationToken>> keys =
tokens.stream()
.map(token -> VKey.create(AllocationToken.class, token))
.map(token -> VKey.createSql(AllocationToken.class, token))
.collect(toImmutableSet());
ImmutableSet<VKey<AllocationToken>> nonexistentKeys =
tm().transact(
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="urn:google:params:xml:ns:packageToken-1.0"
xmlns:packageToken="urn:google:params:xml:ns:packageToken-1.0"
xmlns="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<annotation>
<documentation>
Extensible Provisioning Protocol v1.0
Package Token Extension.
</documentation>
</annotation>
<!-- Element used in info command request to get a package token for a domain. -->
<element name="info"
type="packageToken:infoType"/>
<complexType name="infoType"/>
<!-- Element returned in an info command response with a package token for a package domain. -->
<element name="packageData"
type="packageToken:packageDataType"/>
<complexType name="packageDataType">
<sequence>
<element name="token" type="packageToken:tokenType" maxOccurs="unbounded"/>
</sequence>
</complexType>
<simpleType name="tokenType">
<restriction base="string">
</restriction>
</simpleType>
<!-- End of schema.-->
</schema>
@@ -1352,7 +1352,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
private void assertAllocationTokenWasRedeemed(String token) throws Exception {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry())
.hasValue(
@@ -1362,7 +1362,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
private void assertAllocationTokenWasNotRedeemed(String token) {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isFalse();
}
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD;
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
import static google.registry.testing.DatabaseHelper.createTld;
@@ -31,6 +32,8 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio
import static google.registry.testing.TestDataHelper.updateSubstitutions;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.xml.XmlTestUtils.assertXmlEquals;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -62,14 +65,18 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.host.Host;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper;
import google.registry.xml.ValidationMode;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.joda.money.Money;
@@ -1075,4 +1082,77 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
assertIcannReportingActivityFieldLogged("srs-dom-info");
assertTldsFieldLogged("tld");
}
@Test
void testPackageInfoExtension_returnsPackageInfo() throws Exception {
persistTestEntities(false);
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.PACKAGE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("foo"))
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
.setDiscountFraction(1)
.build());
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
persistResource(domain);
setEppInput("domain_info_package.xml");
doSuccessfulTest("domain_info_response_package.xml", false);
}
@Test
void testPackageInfoExtension_returnsPackageInfoForSuperUser() throws Exception {
persistTestEntities(false);
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.PACKAGE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("foo"))
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
.setDiscountFraction(1)
.build());
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
persistResource(domain);
sessionMetadata.setRegistrarId("TheRegistrar");
setEppInput("domain_info_package.xml");
EppOutput output = runFlowAsSuperuser();
String expectedOutput =
loadFile(
"domain_info_response_superuser_package.xml",
updateSubstitutions(ImmutableMap.of(), "ROID", "2FF-TLD"));
assertXmlEquals(expectedOutput, new String(marshal(output, ValidationMode.LENIENT), UTF_8));
}
@Test
void testPackageInfoExtension_nameNotInPackage() throws Exception {
setEppInput("domain_info_package.xml");
doSuccessfulTest("domain_info_response_empty_package.xml");
}
@Test
void testPackageInfoExtension_notCurrentSponsorRegistrar() throws Exception {
persistTestEntities(false);
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(TokenType.PACKAGE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("foo"))
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
.setDiscountFraction(1)
.build());
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
persistResource(domain);
sessionMetadata.setRegistrarId("TheRegistrar");
setEppInput("domain_info_package.xml");
doSuccessfulTest("domain_info_response_unauthorized.xml", false);
}
}
@@ -20,6 +20,7 @@ import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWit
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@@ -48,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.truth.Truth8;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException;
import google.registry.flows.EppRequestSource;
@@ -73,6 +75,8 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemovePackageTokenOnPackageDomainException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemovePackageTokenOnNonPackageDomainException;
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
@@ -328,7 +332,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
private void assertAllocationTokenWasNotRedeemed(String token) {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isFalse();
}
@@ -591,7 +595,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testSuccess_allocationToken() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
AllocationToken allocationToken =
persistResource(
@@ -611,7 +616,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testSuccess_allocationTokenMultiUse() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
@@ -622,7 +628,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
clock.advanceOneMilli();
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2"));
ImmutableMap.of("DOMAIN", "other-example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
runFlowAssertResponse(
loadFile(
@@ -633,7 +639,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_invalidAllocationToken() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
EppException thrown = assertThrows(InvalidAllocationTokenException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -642,7 +649,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_allocationTokenIsForADifferentDomain() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder()
@@ -660,7 +668,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_promotionNotActive() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder()
@@ -682,7 +691,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_promoTokenNotValidForRegistrar() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder()
@@ -706,7 +716,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_promoTokenNotValidForTld() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder()
@@ -730,7 +741,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
void testFailure_alreadyRedemeedAllocationToken() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
Domain domain = persistActiveDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
@@ -1185,4 +1197,101 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
TransactionReportField.netRenewsFieldFromYears(5),
1));
}
@Test
void testFailsPackageDomainInvalidAllocationToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
EppException thrown =
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailsToRenewPackageDomainNoRemovePackageToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput("domain_renew.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "5"));
EppException thrown =
assertThrows(MissingRemovePackageTokenOnPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailsToRenewNonPackageDomainWithRemovePackageToken() throws Exception {
persistDomain();
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEPACKAGE__"));
EppException thrown =
assertThrows(RemovePackageTokenOnNonPackageDomainException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccesfullyAppliesRemovePackageToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain(SPECIFIED, Money.of(USD, 2));
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEPACKAGE__"));
doSuccessfulTest(
"domain_renew_response.xml",
2,
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00Z"));
// We still need to verify that package token is removed as it's not being tested as a part of
// doSuccessfulTest
Domain domain = reloadResourceByForeignKey();
Truth8.assertThat(domain.getCurrentPackageToken()).isEmpty();
}
}
@@ -20,17 +20,13 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.server.Lock;
import google.registry.model.server.ServerSecret;
import google.registry.testing.TestObject;
import org.junit.jupiter.api.Test;
@@ -50,16 +46,12 @@ public class ClassPathManagerTest {
*/
assertThat(ClassPathManager.getClass("ForeignKeyContactIndex"))
.isEqualTo(ForeignKeyContactIndex.class);
assertThat(ClassPathManager.getClass("AllocationToken")).isEqualTo(AllocationToken.class);
assertThat(ClassPathManager.getClass("RdeRevision")).isEqualTo(RdeRevision.class);
assertThat(ClassPathManager.getClass("Host")).isEqualTo(Host.class);
assertThat(ClassPathManager.getClass("Registrar")).isEqualTo(Registrar.class);
assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class);
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
assertThat(ClassPathManager.getClass("EppResourceIndexBucket"))
.isEqualTo(EppResourceIndexBucket.class);
assertThat(ClassPathManager.getClass("EntityGroupRoot")).isEqualTo(EntityGroupRoot.class);
assertThat(ClassPathManager.getClass("Lock")).isEqualTo(Lock.class);
assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class);
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
assertThat(ClassPathManager.getClass("ForeignKeyHostIndex"))
@@ -102,17 +94,13 @@ public class ClassPathManagerTest {
*/
assertThat(ClassPathManager.getClassName(ForeignKeyContactIndex.class))
.isEqualTo("ForeignKeyContactIndex");
assertThat(ClassPathManager.getClassName(AllocationToken.class)).isEqualTo("AllocationToken");
assertThat(ClassPathManager.getClassName(RdeRevision.class)).isEqualTo("RdeRevision");
assertThat(ClassPathManager.getClassName(Host.class)).isEqualTo("Host");
assertThat(ClassPathManager.getClassName(Registrar.class)).isEqualTo("Registrar");
assertThat(ClassPathManager.getClassName(Contact.class)).isEqualTo("Contact");
assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class))
.isEqualTo("GaeUserIdConverter");
assertThat(ClassPathManager.getClassName(EppResourceIndexBucket.class))
.isEqualTo("EppResourceIndexBucket");
assertThat(ClassPathManager.getClassName(EntityGroupRoot.class)).isEqualTo("EntityGroupRoot");
assertThat(ClassPathManager.getClassName(Lock.class)).isEqualTo("Lock");
assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain");
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
assertThat(ClassPathManager.getClassName(ForeignKeyHostIndex.class))
@@ -222,8 +222,7 @@ public class AllocationTokenTest extends EntityTestCase {
.setToken("abc123")
.setTokenType(TokenType.PACKAGE)
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT);
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> builder.build());
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Package tokens must have renewalPriceBehavior set to SPECIFIED");
@@ -280,7 +279,7 @@ public class AllocationTokenTest extends EntityTestCase {
@Test
void testBuild_onlyOneClientInPackage() {
Buildable.Builder builder =
Buildable.Builder<AllocationToken> builder =
new AllocationToken.Builder()
.setToken("foobar")
.setTokenType(PACKAGE)
@@ -531,7 +530,7 @@ public class AllocationTokenTest extends EntityTestCase {
}
private void assertTerminal(TokenStatus status) {
// The "terminal" message is slightly different so it must be tested separately
// The "terminal" message is slightly different, so it must be tested separately
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
@@ -121,8 +121,7 @@ public class RdeRevisionTest extends EntityTestCase {
}
public static void save(String tld, DateTime date, RdeMode mode, int revision) {
String triplet = RdeNamingUtils.makePartialName(tld, date, mode);
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
RdeRevision object = RdeRevision.create(tld, date.toLocalDate(), mode, revision);
tm().transact(() -> tm().put(object));
}
}
@@ -49,7 +49,8 @@ public class LockTest extends EntityTestCase {
super(JpaEntityCoverageCheck.ENABLED);
}
private Optional<Lock> acquire(String tld, Duration leaseLength, LockState expectedLockState) {
private static Optional<Lock> acquire(
String tld, Duration leaseLength, LockState expectedLockState) {
Lock.lockMetrics = mock(LockMetrics.class);
Optional<Lock> lock = Lock.acquire(RESOURCE_NAME, tld, leaseLength, requestStatusChecker, true);
verify(Lock.lockMetrics).recordAcquire(RESOURCE_NAME, tld, expectedLockState);
@@ -58,7 +59,7 @@ public class LockTest extends EntityTestCase {
return lock;
}
private void release(Lock lock, String expectedTld, long expectedMillis) {
private static void release(Lock lock, String expectedTld, long expectedMillis) {
Lock.lockMetrics = mock(LockMetrics.class);
lock.release();
verify(Lock.lockMetrics)
@@ -44,7 +44,7 @@ final class LockHandlerImplTest {
final AppEngineExtension appEngine = AppEngineExtension.builder().withCloudSql().build();
private static class CountingCallable implements Callable<Void> {
int numCalled = 0;
int numCalled;
@Override
public Void call() {
@@ -69,16 +69,11 @@ final class LockHandlerImplTest {
}
}
private boolean executeWithLocks(Callable<Void> callable, final @Nullable Lock acquiredLock) {
private boolean executeWithLocks(Callable<Void> callable, @Nullable final Lock acquiredLock) {
return createTestLockHandler(acquiredLock)
.executeWithLocks(callable, "tld", ONE_DAY, "resourceName");
}
private boolean executeWithSqlLocks(Callable<Void> callable, final @Nullable Lock acquiredLock) {
return createTestLockHandler(acquiredLock)
.executeWithSqlLocks(callable, "tld", ONE_DAY, "resourceName");
}
@Test
void testLockSucceeds() {
Lock lock = mock(Lock.class);
@@ -88,15 +83,6 @@ final class LockHandlerImplTest {
verify(lock, times(1)).release();
}
@Test
void testSqlLockSucceeds() {
Lock lock = mock(Lock.class);
CountingCallable countingCallable = new CountingCallable();
assertThat(executeWithSqlLocks(countingCallable, lock)).isTrue();
assertThat(countingCallable.numCalled).isEqualTo(1);
verify(lock, times(1)).release();
}
@Test
void testLockSucceeds_uncheckedException() {
Lock lock = mock(Lock.class);
@@ -157,11 +143,6 @@ final class LockHandlerImplTest {
assertThat(leaseLength).isEqualTo(ONE_DAY);
return Optional.ofNullable(acquiredLock);
}
@Override
Optional<Lock> acquireSql(String resourceName, String tld, Duration leaseLength) {
return acquire(resourceName, tld, leaseLength);
}
};
}
}
@@ -42,12 +42,6 @@ public class FakeLockHandler implements LockHandler {
return execute(callable);
}
@Override
public boolean executeWithSqlLocks(
Callable<Void> callable, @Nullable String tld, Duration leaseLength, String... lockNames) {
return execute(callable);
}
private boolean execute(Callable<Void> callable) {
if (!lockSucceeds) {
return false;
@@ -28,13 +28,13 @@ import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import google.registry.model.ImmutableObject;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -119,19 +119,20 @@ public class MutatingCommandTest {
@Test
void testSuccess_create() throws Exception {
ImmutableList<VKey<?>> keys =
Arrays.asList(host1, host2, registrar1, registrar2).stream()
.map(entity -> VKey.from(Key.create(entity)))
Stream.of(host1, host2, registrar1, registrar2)
.map(ImmutableObject::createVKey)
.collect(toImmutableList());
tm().transact(() -> tm().delete(keys));
MutatingCommand command = new MutatingCommand() {
@Override
protected void init() {
stageEntityChange(null, newHost1);
stageEntityChange(null, newHost2);
stageEntityChange(null, newRegistrar1);
stageEntityChange(null, newRegistrar2);
}
};
MutatingCommand command =
new MutatingCommand() {
@Override
protected void init() {
stageEntityChange(null, newHost1);
stageEntityChange(null, newHost2);
stageEntityChange(null, newRegistrar1);
stageEntityChange(null, newRegistrar2);
}
};
command.init();
String changes = command.prompt();
assertThat(changes)
@@ -366,7 +366,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
"--registrar=blobio",
"--email=contact@email.com",
"--certfile=" + getCertFilename()));
assertThat(thrown).hasMessageThat().contains("VKey<Registrar>(sql:blobio-1,ofy:blobio-1)");
assertThat(thrown).hasMessageThat().contains("VKey<Registrar>(sql:blobio-1)");
}
@Test
@@ -0,0 +1,16 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<domain:info
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name hosts="all">example.tld</domain:name>
</domain:info>
</info>
<extension>
<packageToken:info
xmlns:packageToken=
"urn:google:params:xml:ns:packageToken-1.0" />
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<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:registrant>jd1234</domain:registrant>
<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>
<extension>
<packageToken:packageData xmlns:packageToken=
"urn:google:params:xml:ns:packageToken-1.0">
<packageToken:token></packageToken:token>
</packageToken:packageData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,45 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<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:registrant>jd1234</domain:registrant>
<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>
<extension>
<packageToken:packageData xmlns:packageToken=
"urn:google:params:xml:ns:packageToken-1.0">
<packageToken:token>abc123</packageToken:token>
</packageToken:packageData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,26 @@
<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:registrant>jd1234</domain:registrant>
<domain:clID>NewRegistrar</domain:clID>
</domain:infData>
</resData>
<extension>
<packageToken:packageData xmlns:packageToken=
"urn:google:params:xml:ns:packageToken-1.0">
<packageToken:token>abc123</packageToken:token>
</packageToken:packageData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -12,7 +12,7 @@
<allocationToken:allocationToken
xmlns:allocationToken=
"urn:ietf:params:xml:ns:allocationToken-1.0">
abc123
%TOKEN%
</allocationToken:allocationToken>
</extension>
<clTRID>ABC-12345</clTRID>
@@ -89,7 +89,6 @@ class google.registry.model.domain.Domain {
google.registry.persistence.VKey<google.registry.model.contact.Contact> billingContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> registrantContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> techContact;
google.registry.persistence.VKey<google.registry.model.domain.token.AllocationToken> currentPackageToken;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$Autorenew> autorenewPollMessage;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$OneTime> deletePollMessage;
java.lang.String creationClientId;
@@ -122,7 +121,6 @@ class google.registry.model.domain.DomainBase {
google.registry.persistence.VKey<google.registry.model.contact.Contact> billingContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> registrantContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> techContact;
google.registry.persistence.VKey<google.registry.model.domain.token.AllocationToken> currentPackageToken;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$Autorenew> autorenewPollMessage;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$OneTime> deletePollMessage;
java.lang.String creationClientId;
@@ -216,35 +214,6 @@ class google.registry.model.domain.secdns.DomainDsDataHistory {
java.lang.Long domainHistoryRevisionId;
java.lang.Long dsDataHistoryRevisionId;
}
class google.registry.model.domain.token.AllocationToken {
@Id java.lang.String token;
boolean discountPremiums;
double discountFraction;
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> tokenStatusTransitions;
google.registry.model.domain.token.AllocationToken$RegistrationBehavior registrationBehavior;
google.registry.model.domain.token.AllocationToken$TokenType tokenType;
google.registry.persistence.DomainHistoryVKey redemptionHistoryEntry;
int discountYears;
java.lang.String domainName;
java.util.Set<java.lang.String> allowedClientIds;
java.util.Set<java.lang.String> allowedTlds;
}
enum google.registry.model.domain.token.AllocationToken$RegistrationBehavior {
ANCHOR_TENANT;
BYPASS_TLD_STATE;
DEFAULT;
}
enum google.registry.model.domain.token.AllocationToken$TokenStatus {
CANCELLED;
ENDED;
NOT_STARTED;
VALID;
}
enum google.registry.model.domain.token.AllocationToken$TokenType {
PACKAGE;
SINGLE_USE;
UNLIMITED_USE;
}
class google.registry.model.eppcommon.AuthInfo$PasswordAuth {
java.lang.String repoId;
java.lang.String value;
@@ -343,58 +312,6 @@ class google.registry.model.index.ForeignKeyIndex$ForeignKeyHostIndex {
google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime;
}
class google.registry.model.rde.RdeRevision {
@Id java.lang.String id;
int revision;
}
class google.registry.model.registrar.Registrar {
@Id java.lang.String clientIdentifier;
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
boolean blockPremiumNames;
boolean contactsRequireSyncing;
boolean registryLockAllowed;
google.registry.model.registrar.Registrar$State state;
google.registry.model.registrar.Registrar$Type type;
java.lang.Long ianaIdentifier;
java.lang.String clientCertificate;
java.lang.String clientCertificateHash;
java.lang.String driveFolderId;
java.lang.String emailAddress;
java.lang.String failoverClientCertificate;
java.lang.String failoverClientCertificateHash;
java.lang.String faxNumber;
java.lang.String icannReferralEmail;
java.lang.String passwordHash;
java.lang.String phoneNumber;
java.lang.String phonePasscode;
java.lang.String poNumber;
java.lang.String registrarName;
java.lang.String salt;
java.lang.String url;
java.lang.String whoisServer;
java.util.List<google.registry.util.CidrAddressBlock> ipAddressWhitelist;
java.util.Map<org.joda.money.CurrencyUnit, java.lang.String> billingAccountMap;
java.util.Set<java.lang.String> allowedTlds;
java.util.Set<java.lang.String> rdapBaseUrls;
org.joda.time.DateTime lastCertificateUpdateTime;
org.joda.time.DateTime lastExpiringCertNotificationSentDate;
org.joda.time.DateTime lastExpiringFailoverCertNotificationSentDate;
}
enum google.registry.model.registrar.Registrar$State {
ACTIVE;
DISABLED;
PENDING;
SUSPENDED;
}
enum google.registry.model.registrar.Registrar$Type {
EXTERNAL_MONITORING;
INTERNAL;
MONITORING;
OTE;
PDT;
REAL;
TEST;
}
class google.registry.model.reporting.DomainTransactionRecord {
google.registry.model.reporting.DomainTransactionRecord$TransactionReportField reportField;
java.lang.Integer reportAmount;
@@ -472,14 +389,6 @@ enum google.registry.model.reporting.HistoryEntry$Type {
RDE_IMPORT;
SYNTHETIC;
}
class google.registry.model.server.Lock {
@Id java.lang.String lockId;
java.lang.String requestLogId;
java.lang.String resourceName;
java.lang.String tld;
org.joda.time.DateTime acquiredTime;
org.joda.time.DateTime expirationTime;
}
class google.registry.model.server.ServerSecret {
@Id long id;
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
@@ -513,7 +422,3 @@ enum google.registry.model.transfer.TransferStatus {
SERVER_APPROVED;
SERVER_CANCELLED;
}
class google.registry.persistence.DomainHistoryVKey {
java.lang.Long historyRevisionId;
java.lang.String repoId;
}
@@ -261,19 +261,19 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2022-08-20 00:16:00.917251</td>
<td class="property_value">2022-08-30 20:29:57.165769</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
<td id="lastFlywayFile" class="property_value">V126__drop_autorenew_poll_message_history_id_column_in_domain_table.sql</td>
<td id="lastFlywayFile" class="property_value">V127__fix_user.sql</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<svg viewbox="0.00 0.00 4029.00 3065.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3061)">
<svg viewbox="0.00 0.00 4029.00 3084.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 3080.5)">
<title>SchemaCrawler_Diagram</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-3061 4025,-3061 4025,4 -4,4" />
<polygon fill="white" stroke="transparent" points="-4,4 -4,-3080.5 4025,-3080.5 4025,4 -4,4" />
<text text-anchor="start" x="3752.5" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
generated by
</text>
@@ -284,7 +284,7 @@ td.section {
generated on
</text>
<text text-anchor="start" x="3835.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
2022-08-20 00:16:00.917251
2022-08-30 20:29:57.165769
</text>
<polygon fill="none" stroke="#888888" points="3748,-4 3748,-44 4013,-44 4013,-4 3748,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
@@ -3164,39 +3164,44 @@ td.section {
</g> <!-- user_f2216f01 -->
<g id="node39" class="node">
<title>user_f2216f01</title>
<polygon fill="#ebcef2" stroke="transparent" points="3738.5,-3033 3738.5,-3052 3839.5,-3052 3839.5,-3033 3738.5,-3033" />
<text text-anchor="start" x="3740.5" y="-3039.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
<polygon fill="#ebcef2" stroke="transparent" points="3720.5,-3052 3720.5,-3071 3821.5,-3071 3821.5,-3052 3720.5,-3052" />
<text text-anchor="start" x="3722.5" y="-3058.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
public."User"
</text>
<polygon fill="#ebcef2" stroke="transparent" points="3839.5,-3033 3839.5,-3052 3913.5,-3052 3913.5,-3033 3839.5,-3033" />
<text text-anchor="start" x="3874.5" y="-3038.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<polygon fill="#ebcef2" stroke="transparent" points="3821.5,-3052 3821.5,-3071 3931.5,-3071 3931.5,-3052 3821.5,-3052" />
<text text-anchor="start" x="3892.5" y="-3057.8" font-family="Helvetica,sans-Serif" font-size="14.00">
[table]
</text>
<text text-anchor="start" x="3740.5" y="-3020.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
<text text-anchor="start" x="3722.5" y="-3039.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
id
</text>
<text text-anchor="start" x="3833.5" y="-3019.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3815.5" y="-3038.8" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="3841.5" y="-3019.8" font-family="Helvetica,sans-Serif" font-size="14.00">
int8 not null
<text text-anchor="start" x="3823.5" y="-3038.8" font-family="Helvetica,sans-Serif" font-size="14.00">
bigserial not null
</text>
<text text-anchor="start" x="3740.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3815.5" y="-3019.8" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="3823.5" y="-3019.8" font-family="Helvetica,sans-Serif" font-size="14.00">
auto-incremented
</text>
<text text-anchor="start" x="3722.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
email_address
</text>
<text text-anchor="start" x="3833.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3815.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="3841.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3823.5" y="-3000.8" font-family="Helvetica,sans-Serif" font-size="14.00">
text not null
</text>
<text text-anchor="start" x="3740.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3722.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
gaia_id
</text>
<text text-anchor="start" x="3833.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3815.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="3841.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="3823.5" y="-2981.8" font-family="Helvetica,sans-Serif" font-size="14.00">
text not null
</text>
<polygon fill="none" stroke="#888888" points="3737,-2975 3737,-3053 3914,-3053 3914,-2975 3737,-2975" />
<polygon fill="none" stroke="#888888" points="3719,-2975.5 3719,-3072.5 3932,-3072.5 3932,-2975.5 3719,-2975.5" />
</g>
</g>
</svg>
@@ -7011,7 +7016,12 @@ td.section {
<tr>
<td class="spacer"></td>
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">auto-incremented</td>
</tr>
<tr>
<td class="spacer"></td>
@@ -261,19 +261,19 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2022-08-20 00:15:58.476902</td>
<td class="property_value">2022-08-30 20:29:55.057361</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
<td id="lastFlywayFile" class="property_value">V126__drop_autorenew_poll_message_history_id_column_in_domain_table.sql</td>
<td id="lastFlywayFile" class="property_value">V127__fix_user.sql</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<svg viewbox="0.00 0.00 4713.00 7133.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 7129)">
<svg viewbox="0.00 0.00 4713.00 7171.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="erDiagram" style="overflow: hidden; width: 100%; height: 800px"><g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 7167)">
<title>SchemaCrawler_Diagram</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-7129 4709,-7129 4709,4 -4,4" />
<polygon fill="white" stroke="transparent" points="-4,4 -4,-7167 4709,-7167 4709,4 -4,4" />
<text text-anchor="start" x="4436.5" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">
generated by
</text>
@@ -284,7 +284,7 @@ td.section {
generated on
</text>
<text text-anchor="start" x="4519.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
2022-08-20 00:15:58.476902
2022-08-30 20:29:55.057361
</text>
<polygon fill="none" stroke="#888888" points="4432,-4 4432,-44 4697,-44 4697,-4 4432,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
@@ -6484,79 +6484,92 @@ td.section {
</g> <!-- user_f2216f01 -->
<g id="node39" class="node">
<title>user_f2216f01</title>
<polygon fill="#ebcef2" stroke="transparent" points="4323,-7100.5 4323,-7119.5 4513,-7119.5 4513,-7100.5 4323,-7100.5" />
<text text-anchor="start" x="4325" y="-7107.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
<polygon fill="#ebcef2" stroke="transparent" points="4318,-7138.5 4318,-7157.5 4508,-7157.5 4508,-7138.5 4318,-7138.5" />
<text text-anchor="start" x="4320" y="-7145.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
public."User"
</text>
<polygon fill="#ebcef2" stroke="transparent" points="4513,-7100.5 4513,-7119.5 4613,-7119.5 4613,-7100.5 4513,-7100.5" />
<text text-anchor="start" x="4574" y="-7106.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<polygon fill="#ebcef2" stroke="transparent" points="4508,-7138.5 4508,-7157.5 4618,-7157.5 4618,-7138.5 4508,-7138.5" />
<text text-anchor="start" x="4579" y="-7144.3" font-family="Helvetica,sans-Serif" font-size="14.00">
[table]
</text>
<text text-anchor="start" x="4325" y="-7088.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
<text text-anchor="start" x="4320" y="-7126.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-style="italic" font-size="14.00">
id
</text>
<text text-anchor="start" x="4507" y="-7087.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7125.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-7087.3" font-family="Helvetica,sans-Serif" font-size="14.00">
int8 not null
<text text-anchor="start" x="4510" y="-7125.3" font-family="Helvetica,sans-Serif" font-size="14.00">
bigserial not null
</text>
<text text-anchor="start" x="4325" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7106.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4510" y="-7106.3" font-family="Helvetica,sans-Serif" font-size="14.00">
auto-incremented
</text>
<text text-anchor="start" x="4320" y="-7087.3" font-family="Helvetica,sans-Serif" font-size="14.00">
email_address
</text>
<text text-anchor="start" x="4507" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7087.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-7087.3" font-family="Helvetica,sans-Serif" font-size="14.00">
text not null
</text>
<text text-anchor="start" x="4325" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
gaia_id
</text>
<text text-anchor="start" x="4507" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-7068.3" font-family="Helvetica,sans-Serif" font-size="14.00">
text not null
</text>
<text text-anchor="start" x="4325" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
registry_lock_password_hash
</text>
<text text-anchor="start" x="4507" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-7049.3" font-family="Helvetica,sans-Serif" font-size="14.00">
text
</text>
<text text-anchor="start" x="4325" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
registry_lock_password_salt
</text>
<text text-anchor="start" x="4507" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-7030.3" font-family="Helvetica,sans-Serif" font-size="14.00">
text
</text>
<text text-anchor="start" x="4325" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
global_role
</text>
<text text-anchor="start" x="4507" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-7011.3" font-family="Helvetica,sans-Serif" font-size="14.00">
text not null
</text>
<text text-anchor="start" x="4325" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
is_admin
</text>
<text text-anchor="start" x="4507" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-6992.3" font-family="Helvetica,sans-Serif" font-size="14.00">
bool not null
</text>
<text text-anchor="start" x="4325" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4320" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
registrar_roles
</text>
<text text-anchor="start" x="4507" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4502" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4515" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
<text text-anchor="start" x="4510" y="-6973.3" font-family="Helvetica,sans-Serif" font-size="14.00">
"hstore" not null
</text>
<polygon fill="none" stroke="#888888" points="4322,-6948 4322,-7121 4614,-7121 4614,-6948 4322,-6948" />
<text text-anchor="start" x="4320" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
update_timestamp
</text>
<text text-anchor="start" x="4502" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
</text>
<text text-anchor="start" x="4510" y="-6954.3" font-family="Helvetica,sans-Serif" font-size="14.00">
timestamptz
</text>
<polygon fill="none" stroke="#888888" points="4317,-6948 4317,-7159 4619,-7159 4619,-6948 4317,-6948" />
</g>
</g>
</svg>
@@ -14224,7 +14237,12 @@ td.section {
<tr>
<td class="spacer"></td>
<td class="minwidth"><b><i>id</i></b></td>
<td class="minwidth">int8 not null</td>
<td class="minwidth">bigserial not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth"></td>
<td class="minwidth">auto-incremented</td>
</tr>
<tr>
<td class="spacer"></td>
@@ -14261,6 +14279,11 @@ td.section {
<td class="minwidth">registrar_roles</td>
<td class="minwidth">"hstore" not null</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">update_timestamp</td>
<td class="minwidth">timestamptz</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
@@ -14300,6 +14323,18 @@ td.section {
<tr>
<td colspan="3"></td>
</tr>
<tr>
<td colspan="2" class="name">user_unique_email</td>
<td class="description right">[unique index]</td>
</tr>
<tr>
<td class="spacer"></td>
<td class="minwidth">email_address</td>
<td class="minwidth">ascending</td>
</tr>
<tr>
<td colspan="3"></td>
</tr>
<tr>
<td colspan="2" class="name">user_email_address_idx</td>
<td class="description right">[non-unique index]</td>
+1
View File
@@ -124,3 +124,4 @@ V123__drop_unused_columns_in_billing_cancellation_table.sql
V124__add_console_user.sql
V125__create_package_promotion.sql
V126__drop_autorenew_poll_message_history_id_column_in_domain_table.sql
V127__fix_user.sql
@@ -0,0 +1,26 @@
-- Copyright 2022 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.
CREATE SEQUENCE "User_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE "User_id_seq" OWNED BY "User".id;
ALTER TABLE "User" ALTER COLUMN id SET DEFAULT nextval('"User_id_seq"'::regclass);
ALTER TABLE "User" ADD COLUMN update_timestamp timestamptz;
ALTER TABLE "User" ADD CONSTRAINT user_unique_email UNIQUE(email_address);
@@ -1066,10 +1066,30 @@ CREATE TABLE public."User" (
registry_lock_password_salt text,
global_role text NOT NULL,
is_admin boolean NOT NULL,
registrar_roles public.hstore NOT NULL
registrar_roles public.hstore NOT NULL,
update_timestamp timestamp with time zone
);
--
-- Name: User_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."User_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: User_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."User_id_seq" OWNED BY public."User".id;
--
-- Name: ClaimsList revision_id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1119,6 +1139,13 @@ ALTER TABLE ONLY public."SignedMarkRevocationList" ALTER COLUMN revision_id SET
ALTER TABLE ONLY public."Spec11ThreatMatch" ALTER COLUMN id SET DEFAULT nextval('public."SafeBrowsingThreat_id_seq"'::regclass);
--
-- Name: User id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."User" ALTER COLUMN id SET DEFAULT nextval('public."User_id_seq"'::regclass);
--
-- Name: AllocationToken AllocationToken_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -1439,6 +1466,14 @@ ALTER TABLE ONLY public."DomainHistoryHost"
ADD CONSTRAINT ukt2e7ae3t8gcsxd13wjx2ka7ij UNIQUE (domain_history_history_revision_id, domain_history_domain_repo_id, host_repo_id);
--
-- Name: User user_unique_email; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."User"
ADD CONSTRAINT user_unique_email UNIQUE (email_address);
--
-- Name: allocation_token_domain_name_idx; Type: INDEX; Schema: public; Owner: -
--
+2
View File
@@ -497,6 +497,8 @@ comes in at the exact millisecond that the domain would have expired.
* Resource status prohibits this operation.
* The allocation token is not currently valid.
* 2305
* The __REMOVEPACKAGE__ token is missing on a renew package domain command
* The __REMOVEPACKAGE__ token is not allowed on non package domains
* The allocation token is not valid for this domain.
* The allocation token is not valid for this registrar.
* The allocation token is not valid for this TLD.