mirror of
https://github.com/google/nomulus
synced 2026-07-06 08:06:33 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49ade014ab | |||
| b8d901effe | |||
| 23520048dc | |||
| 37ed6c925c | |||
| 17a21f3326 | |||
| f623da9948 | |||
| ddc4a615db | |||
| 06a1fc0022 |
@@ -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()))
|
||||
|
||||
+93
-19
@@ -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.
|
||||
|
||||
+23
@@ -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 {}
|
||||
+45
@@ -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>
|
||||
+45
@@ -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>
|
||||
+26
@@ -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>
|
||||
+1
-1
@@ -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> </p>
|
||||
<p> </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> </p>
|
||||
<p> </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>
|
||||
|
||||
@@ -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: -
|
||||
--
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user