1
0
mirror of https://github.com/google/nomulus synced 2026-05-25 01:01:57 +00:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Weimin Yu
ce2f98f680 Work around Spinnaker issue wrt variables (#465)
* Work around Spinnaker issue wrt variables

Cloud Build variable reference need to stay from the  ${var} pattern
to prevent Spinnaker from trying to resolve it. In all files that
are used by Spinnaker, we change variable reference to the $var form.

We made the minimum amount of change possible, and will review this
issue after the permanent solution is available.
2020-01-30 13:28:36 -05:00
gbrodman
daaf231d39 Add RegistryLockVerifyAction (#461)
* Add RegistryLockVerifyAction

The action takes two parameters
- isLock is a boolean, determining whether we're trying to lock or
unlock a domain
- lockVerificationCode is the UUID by which we'll look up the lock
object in question.

The lock in question must not be expired and must be in a valid lockable
/ unlockable state

* Some responses to CR

* Add slash and move test method

* Add more data and tests

* Fix screenshot
2020-01-29 16:36:39 -05:00
Shicong Huang
955c3d9aeb Fix money conversion for JPY in PremiumListDao (#463) 2020-01-29 13:10:58 -05:00
Shicong Huang
d03cea2443 Add JPA annotations to class Registrar (#430)
* Add JPA annotations to class Registrar

* Use array for Java list

* Exclude parent field

* Use 3 columns for address and use text for enum

* Use EnumParameter and 3 properties in Address

* Rename columns and rebase on HEAD
2020-01-29 11:06:23 -05:00
42 changed files with 1499 additions and 77 deletions

View File

@@ -61,6 +61,11 @@
<url-pattern>/registry-lock-get</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>frontend-servlet</servlet-name>
<url-pattern>/registry-lock-verify</url-pattern>
</servlet-mapping>
<!-- Security config -->
<security-constraint>
<web-resource-collection>

View File

@@ -15,16 +15,22 @@
package google.registry.model.eppcommon;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import java.util.List;
import java.util.Map;
import javax.persistence.Embeddable;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
@@ -42,12 +48,21 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* @see google.registry.model.registrar.RegistrarAddress
*/
@XmlTransient
@Embeddable
@MappedSuperclass
public class Address extends ImmutableObject implements Jsonifiable {
/** The schema validation will enforce that this has 3 lines at most. */
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
@Transient
List<String> street;
@Ignore String streetLine1;
@Ignore String streetLine2;
@Ignore String streetLine3;
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
String city;
@@ -64,7 +79,23 @@ public class Address extends ImmutableObject implements Jsonifiable {
String countryCode;
public ImmutableList<String> getStreet() {
return nullToEmptyImmutableCopy(street);
if (street == null && streetLine1 != null) {
return ImmutableList.of(streetLine1, nullToEmpty(streetLine2), nullToEmpty(streetLine3));
} else {
return nullToEmptyImmutableCopy(street);
}
}
public String getStreetLine1() {
return streetLine1;
}
public String getStreetLine2() {
return streetLine2;
}
public String getStreetLine13() {
return streetLine3;
}
public String getCity() {
@@ -139,4 +170,14 @@ public class Address extends ImmutableObject implements Jsonifiable {
return this;
}
}
@OnLoad
void setStreetForCloudSql() {
if (street == null || street.size() == 0) {
return;
}
streetLine1 = street.get(0);
streetLine2 = street.size() >= 2 ? street.get(1) : null;
streetLine3 = street.size() >= 3 ? street.get(2) : null;
}
}

View File

@@ -169,8 +169,8 @@ public enum StatusValue implements EppEnum {
/** Hibernate type for sets of {@link StatusValue}. */
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {
@Override
protected Object convertToElem(Object value) {
return StatusValue.valueOf((String) value);
protected StatusValue convertToElem(String value) {
return StatusValue.valueOf(value);
}
}
}

View File

@@ -85,12 +85,27 @@ import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.hibernate.annotations.Type;
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"),
})
public class Registrar extends ImmutableObject implements Buildable, Jsonifiable {
/** Represents the type of a registrar entity. */
@@ -208,14 +223,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
.doTransactionless(
() -> Maps.uniqueIndex(loadAll(), Registrar::getClientId)));
@Parent
Key<EntityGroupRoot> parent = getCrossTldKey();
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
/**
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
*
* @see <a href="http://tools.ietf.org/html/rfc5730#section-4.2">Shared Structure Schema</a>
* <p>TODO(shicong): Rename this field to clientId
*/
@Id
@javax.persistence.Id
@Column(name = "client_id", nullable = false)
String clientIdentifier;
/**
@@ -229,21 +247,27 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
* Registrars</a>
*/
@Index String registrarName;
@Index
@Column(nullable = false)
String registrarName;
/** The type of this registrar. */
@Column(nullable = false)
Type type;
/** The state of this registrar. */
State state;
/** The set of TLDs which this registrar is allowed to access. */
// TODO(b/147908600): Investigate how to automatically apply user type
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
Set<String> allowedTlds;
/** Host name of WHOIS server. */
String whoisServer;
/** Base URLs for the registrar's RDAP servers. */
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
Set<String> rdapBaseUrls;
/**
@@ -271,12 +295,14 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
String failoverClientCertificateHash;
/** A whitelist of netmasks (in CIDR notation) which the client is allowed to connect from. */
@org.hibernate.annotations.Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
List<CidrAddressBlock> ipAddressWhitelist;
/** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */
String passwordHash;
/** Randomly generated hash salt. */
@Column(name = "password_salt")
String salt;
// The following fields may appear redundant to the above, but are
@@ -287,6 +313,24 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* unrestricted UTF-8.
*/
@IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(
name = "streetLine1",
column = @Column(name = "localized_address_street_line1")),
@AttributeOverride(
name = "streetLine2",
column = @Column(name = "localized_address_street_line2")),
@AttributeOverride(
name = "streetLine3",
column = @Column(name = "localized_address_street_line3")),
@AttributeOverride(name = "city", column = @Column(name = "localized_address_city")),
@AttributeOverride(name = "state", column = @Column(name = "localized_address_state")),
@AttributeOverride(name = "zip", column = @Column(name = "localized_address_zip")),
@AttributeOverride(
name = "countryCode",
column = @Column(name = "localized_address_country_code"))
})
RegistrarAddress localizedAddress;
/**
@@ -294,6 +338,16 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* representable in the 7-bit US-ASCII character set.
*/
@IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")),
@AttributeOverride(name = "streetLine2", column = @Column(name = "i18n_address_street_line2")),
@AttributeOverride(name = "streetLine3", column = @Column(name = "i18n_address_street_line3")),
@AttributeOverride(name = "city", column = @Column(name = "i18n_address_city")),
@AttributeOverride(name = "state", column = @Column(name = "i18n_address_state")),
@AttributeOverride(name = "zip", column = @Column(name = "i18n_address_zip")),
@AttributeOverride(name = "countryCode", column = @Column(name = "i18n_address_country_code"))
})
RegistrarAddress internationalizedAddress;
/** Voice number. */
@@ -309,16 +363,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
/**
* Registrar identifier used for reporting to ICANN.
*
* <ul>
* <li>8 is used for Testing Registrar.
* <li>9997 is used by ICAAN 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>
*
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
* IDs</a>
*/
@Index
@Nullable
Long ianaIdentifier;
@Index @Nullable Long ianaIdentifier;
/** Identifier of registrar used in external billing system (e.g. Oracle). */
@Nullable
@@ -338,18 +393,19 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
*/
@Nullable
@Mapify(CurrencyMapper.class)
@org.hibernate.annotations.Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
Map<CurrencyUnit, BillingAccountEntry> billingAccountMap;
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
@Embed
static class BillingAccountEntry extends ImmutableObject {
public static class BillingAccountEntry extends ImmutableObject {
CurrencyUnit currency;
String accountId;
BillingAccountEntry() {}
BillingAccountEntry(CurrencyUnit currency, String accountId) {
public BillingAccountEntry(CurrencyUnit currency, String accountId) {
this.accountId = accountId;
this.currency = currency;
}
@@ -366,6 +422,11 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
return billingAccountEntry.currency;
}
}
/** Returns the account id of this entry. */
public String getAccountId() {
return accountId;
}
}
/** URL of registrar's website. */
@@ -390,14 +451,10 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
/** An automatically managed last-saved timestamp. */
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/**
* The time that the certificate was last updated.
*/
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;
/**
* Telephone support passcode (5-digit numeric)
*/
/** Telephone support passcode (5-digit numeric) */
String phonePasscode;
/**

View File

@@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.Address;
import javax.persistence.Embeddable;
/**
* Registrar Address
@@ -29,6 +30,7 @@ import google.registry.model.eppcommon.Address;
* classes.
*/
@Embed
@Embeddable
public class RegistrarAddress extends Address {
@Override

View File

@@ -31,6 +31,7 @@ import google.registry.ui.server.registrar.OteStatusAction;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.registrar.RegistrarSettingsAction;
import google.registry.ui.server.registrar.RegistryLockGetAction;
import google.registry.ui.server.registrar.RegistryLockVerifyAction;
/** Dagger component with per-request lifetime for "default" App Engine module. */
@RequestScope
@@ -53,6 +54,8 @@ interface FrontendRequestComponent {
RegistryLockGetAction registryLockGetAction();
RegistryLockVerifyAction registryLockVerifyAction();
@Subcomponent.Builder
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
@Override public abstract Builder requestModule(RequestModule requestModule);

View File

@@ -0,0 +1,35 @@
// Copyright 2020 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.persistence;
import google.registry.util.CidrAddressBlock;
import java.util.List;
/**
* Hibernate {@link org.hibernate.usertype.UserType} for storing/retrieving {@link
* List<CidrAddressBlock>} objects.
*/
public class CidrAddressBlockListUserType extends StringListUserType<CidrAddressBlock> {
@Override
protected CidrAddressBlock convertToElem(String columnValue) {
return columnValue == null ? null : CidrAddressBlock.create(columnValue);
}
@Override
protected String convertToColumn(CidrAddressBlock elementValue) {
return elementValue == null ? null : elementValue.toString();
}
}

View File

@@ -0,0 +1,48 @@
// Copyright 2020 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.persistence;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import google.registry.model.registrar.Registrar.BillingAccountEntry;
import java.util.Map;
import org.hibernate.usertype.UserType;
import org.joda.money.CurrencyUnit;
/**
* A custom {@link UserType} for storing/retrieving {@link Map<CurrencyUnit, BillingAccountEntry>}
* objects.
*/
public class CurrencyToBillingMapUserType extends MapUserType {
@Override
public Object toEntityTypeMap(Map<String, String> map) {
return map.entrySet().stream()
.collect(
toImmutableMap(
entry -> CurrencyUnit.of(entry.getKey()),
entry ->
new BillingAccountEntry(CurrencyUnit.of(entry.getKey()), entry.getValue())));
}
@Override
public Map<String, String> toDbSupportedMap(Object map) {
return ((Map<CurrencyUnit, BillingAccountEntry>) map)
.entrySet().stream()
.collect(
toImmutableMap(
entry -> entry.getKey().getCode(), entry -> entry.getValue().getAccountId()));
}
}

View File

@@ -18,11 +18,12 @@ import java.util.HashSet;
import java.util.Set;
/** Abstract Hibernate user type for storing/retrieving {@link Set<Enum<E>>}. */
public class EnumSetUserType<E extends Enum<E>> extends GenericCollectionUserType<Set<Enum<E>>> {
public class EnumSetUserType<E extends Enum<E>>
extends GenericCollectionUserType<Set<E>, E, String> {
@Override
Set<Enum<E>> getNewCollection() {
return new HashSet<Enum<E>>();
Set<E> getNewCollection() {
return new HashSet<>();
}
@Override

View File

@@ -14,6 +14,7 @@
package google.registry.persistence;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.sql.Array;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -23,8 +24,15 @@ import java.util.Collection;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/** Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL. */
public abstract class GenericCollectionUserType<T extends Collection> extends MutableUserType {
/**
* Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL.
*
* @param <T> the concrete {@link Collection} type of the entity field
* @param <E> the Java type of the element for the collection of the entity field
* @param <C> the JDBC supported type of the element in the DB column array
*/
public abstract class GenericCollectionUserType<T extends Collection<E>, E, C>
extends MutableUserType {
abstract T getNewCollection();
@@ -54,6 +62,11 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
}
}
@Override
public Class returnedClass() {
return new TypeInstantiator<T>(getClass()) {}.getExactType();
}
@Override
public int[] sqlTypes() {
return new int[] {getColumnType().getTypeCode()};
@@ -65,7 +78,7 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
throws HibernateException, SQLException {
if (rs.getArray(names[0]) != null) {
T result = getNewCollection();
for (Object element : (Object[]) rs.getArray(names[0]).getArray()) {
for (C element : (C[]) rs.getArray(names[0]).getArray()) {
result.add(convertToElem(element));
}
return result;
@@ -81,8 +94,12 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
st.setArray(index, null);
return;
}
T list = (T) value;
Array arr = st.getConnection().createArrayOf(getColumnType().getTypeName(), list.toArray());
T collection = (T) value;
Array arr =
st.getConnection()
.createArrayOf(
getColumnType().getTypeName(),
collection.stream().map(this::convertToColumn).toArray());
st.setArray(index, arr);
}
@@ -92,7 +109,11 @@ public abstract class GenericCollectionUserType<T extends Collection> extends Mu
* <p>This method is useful when encoding a java type to one of the types that can be used as an
* array element.
*/
protected Object convertToElem(Object columnValue) {
return columnValue;
protected E convertToElem(C columnValue) {
return (E) columnValue;
}
protected C convertToColumn(E elementValue) {
return (C) elementValue;
}
}

View File

@@ -0,0 +1,34 @@
// Copyright 2020 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.persistence;
import google.registry.util.TypeUtils.TypeInstantiator;
import javax.persistence.AttributeConverter;
/** Generic converter for storing/retrieving {@link Enum} objects. */
public class GenericEnumConverter<T extends Enum<T>> implements AttributeConverter<T, String> {
@Override
public String convertToDatabaseColumn(T attribute) {
return attribute == null ? null : attribute.toString();
}
@Override
public T convertToEntityAttribute(String dbData) {
return dbData == null
? null
: Enum.valueOf(new TypeInstantiator<T>(getClass()) {}.getExactType(), dbData);
}
}

View File

@@ -45,13 +45,29 @@ public class MapUserType extends MutableUserType {
public Object nullSafeGet(
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
return rs.getObject(names[0]);
return toEntityTypeMap((Map<String, String>) rs.getObject(names[0]));
}
@Override
public void nullSafeSet(
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
st.setObject(index, value);
st.setObject(index, toDbSupportedMap(value));
}
/**
* Subclass can override this method to convert the {@link Map<String, String>} to a {@link Map}
* of specific type defined in the entity class.
*/
public Object toEntityTypeMap(Map<String, String> map) {
return map;
}
/**
* Subclass can override this method to convert the {@link Map} of specific type to a {@link
* Map<String, String>} that can be stored in the hstore type column.
*/
public Map<String, String> toDbSupportedMap(Object map) {
return (Map<String, String>) map;
}
}

View File

@@ -0,0 +1,22 @@
// Copyright 2020 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.persistence;
import google.registry.model.registrar.Registrar;
import javax.persistence.Converter;
/** JPA converter for storing/retrieving {@link Registrar.State} objects. */
@Converter(autoApply = true)
public class RegistrarStateConverter extends GenericEnumConverter<Registrar.State> {}

View File

@@ -0,0 +1,22 @@
// Copyright 2020 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.persistence;
import google.registry.model.registrar.Registrar;
import javax.persistence.Converter;
/** JPA converter for storing/retrieving {@link Registrar.Type} objects. */
@Converter(autoApply = true)
public class RegistrarTypeConverter extends GenericEnumConverter<Registrar.Type> {}

View File

@@ -18,10 +18,10 @@ import com.google.common.collect.Lists;
import java.util.List;
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
public class StringListUserType extends GenericCollectionUserType<List<String>> {
public class StringListUserType<E> extends GenericCollectionUserType<List<E>, E, String> {
@Override
List<String> getNewCollection() {
List<E> getNewCollection() {
return Lists.newArrayList();
}
@@ -29,9 +29,4 @@ public class StringListUserType extends GenericCollectionUserType<List<String>>
ArrayColumnType getColumnType() {
return ArrayColumnType.STRING;
}
@Override
public Class returnedClass() {
return List.class;
}
}

View File

@@ -18,10 +18,10 @@ import com.google.common.collect.Sets;
import java.util.Set;
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
public class StringSetUserType extends GenericCollectionUserType<Set<String>> {
public class StringSetUserType<E> extends GenericCollectionUserType<Set<E>, E, String> {
@Override
Set<String> getNewCollection() {
Set<E> getNewCollection() {
return Sets.newHashSet();
}
@@ -29,9 +29,4 @@ public class StringSetUserType extends GenericCollectionUserType<Set<String>> {
ArrayColumnType getColumnType() {
return ArrayColumnType.STRING;
}
@Override
public Class returnedClass() {
return Set.class;
}
}

View File

@@ -156,7 +156,11 @@ public class PremiumListDao {
RevisionIdAndLabel.create(premiumList.getRevisionId(), label);
try {
Optional<BigDecimal> price = PremiumListCache.cachePremiumEntries.get(revisionIdAndLabel);
return price.map(p -> Money.of(premiumList.getCurrency(), p));
return price.map(
p ->
Money.of(
premiumList.getCurrency(),
p.setScale(premiumList.getCurrency().getDecimalPlaces())));
} catch (InvalidCacheLoadException | ExecutionException e) {
throw new RuntimeException(
String.format(

View File

@@ -38,7 +38,7 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
public static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
ImmutableSet.of(
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);

View File

@@ -14,7 +14,7 @@
package google.registry.ui.server.registrar;
import static google.registry.request.RequestParameters.extractBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
@@ -144,4 +144,16 @@ public final class RegistrarConsoleModule {
static Optional<String> provideOptionalPasscode(HttpServletRequest req) {
return extractOptionalParameter(req, "passcode");
}
@Provides
@Parameter("lockVerificationCode")
static String provideLockVerificationCode(HttpServletRequest req) {
return extractRequiredParameter(req, "lockVerificationCode");
}
@Provides
@Parameter("isLock")
static Boolean provideIsLock(HttpServletRequest req) {
return extractBooleanParameter(req, "isLock");
}
}

View File

@@ -0,0 +1,97 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.ui.server.registrar;
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.flogger.FluentLogger;
import com.google.template.soy.tofu.SoyTofu;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.schema.domain.RegistryLock;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.SoyTemplateUtils;
import google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo;
import google.registry.util.Clock;
import java.util.HashMap;
import javax.inject.Inject;
/** Action that allows for verification of registry lock / unlock requests */
@Action(
service = Action.Service.DEFAULT,
path = RegistryLockVerifyAction.PATH,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public final class RegistryLockVerifyAction extends HtmlAction {
public static final String PATH = "/registry-lock-verify";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
SoyTemplateUtils.createTofuSupplier(
google.registry.ui.soy.ConsoleSoyInfo.getInstance(),
google.registry.ui.soy.AnalyticsSoyInfo.getInstance(),
google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo.getInstance());
private final Clock clock;
private final String lockVerificationCode;
private final Boolean isLock;
@Inject
public RegistryLockVerifyAction(
Clock clock,
@Parameter("lockVerificationCode") String lockVerificationCode,
@Parameter("isLock") Boolean isLock) {
this.clock = clock;
this.lockVerificationCode = lockVerificationCode;
this.isLock = isLock;
}
@Override
public void runAfterLogin(HashMap<String, Object> data) {
try {
boolean isAdmin = authResult.userAuthInfo().get().isUserAdmin();
final RegistryLock resultLock;
if (isLock) {
resultLock = DomainLockUtils.verifyAndApplyLock(lockVerificationCode, isAdmin, clock);
} else {
resultLock = DomainLockUtils.verifyAndApplyUnlock(lockVerificationCode, isAdmin, clock);
}
data.put("isLock", isLock);
data.put("success", true);
data.put("fullyQualifiedDomainName", resultLock.getDomainName());
} catch (Throwable t) {
logger.atWarning().withCause(t).log(
"Error when verifying verification code %s", lockVerificationCode);
data.put("success", false);
data.put("errorMessage", Throwables.getRootCause(t).getMessage());
}
response.setPayload(
TOFU_SUPPLIER
.get()
.newRenderer(RegistryLockVerificationSoyInfo.VERIFICATION_PAGE)
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
.setData(data)
.render());
}
@Override
public String getPath() {
return PATH;
}
}

View File

@@ -20,6 +20,7 @@
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
-->
<class>google.registry.model.domain.DomainBase</class>
<class>google.registry.model.registrar.Registrar</class>
<class>google.registry.schema.domain.RegistryLock</class>
<class>google.registry.schema.tmch.ClaimsList</class>
<class>google.registry.schema.cursor.Cursor</class>
@@ -40,6 +41,8 @@
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
<class>google.registry.persistence.CurrencyUnitConverter</class>
<class>google.registry.persistence.DateTimeConverter</class>
<class>google.registry.persistence.RegistrarStateConverter</class>
<class>google.registry.persistence.RegistrarTypeConverter</class>
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
<class>google.registry.persistence.ZonedDateTimeConverter</class>

View File

@@ -0,0 +1,71 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{namespace registry.soy.registrar.registrylock.verification}
/**
* Results page for a registry lock/unlock verification.
*/
{template .verificationPage}
{@param username: string} /** Arbitrary username to display. */
{@param analyticsConfig: [googleAnalyticsId: string|null]}
{@param success: bool}
{@param? errorMessage: string}
{@param? isLock: bool}
{@param? fullyQualifiedDomainName: string}
{call registry.soy.console.header}
{param app: 'registrar' /}
{param subtitle: 'Verify Registry Lock' /}
{param analyticsConfig: $analyticsConfig /}
{/call}
{call registry.soy.console.googlebar data="all" /}
<div id="reg-content-and-footer">
<div id="reg-content">
<h1>Registry Lock Verification</h1>
{if $success}
{call .success data="all" /}
{else}
{call .failure data="all" /}
{/if}
<h3><a href="/registrar">Return to Registrar Console</a></h3>
</div>
{call registry.soy.console.footer /}
</div>
{/template}
/**
* Result page for failure, e.g. the UUID was invalid
*/
{template .failure}
{@param? errorMessage: string}
<h2 class="{css('kd-errormessage')}">Failed: {if isNonnull($errorMessage)}
{$errorMessage}
{else}
Undefined error message
{/if}
</h2>
{/template}
/**
* Result page for a successful lock / unlock.
*/
{template .success}
{@param? isLock: bool}
{@param? fullyQualifiedDomainName: string}
<h3>
Success: {if $isLock}lock{else}unlock{/if} has been applied to {$fullyQualifiedDomainName}
</h3>
{/template}

View File

@@ -0,0 +1,71 @@
// Copyright 2020 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.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableList;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import google.registry.util.CidrAddressBlock;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CidrAddressBlockListUserType}. */
@RunWith(JUnit4.class)
public class CidrAddressBlockListUserTypeTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameCidrAddressBlock() {
List<CidrAddressBlock> addresses =
ImmutableList.of(
CidrAddressBlock.create("0.0.0.0/32"),
CidrAddressBlock.create("255.255.255.254/31"),
CidrAddressBlock.create("::"),
CidrAddressBlock.create("8000::/1"),
CidrAddressBlock.create("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
TestEntity testEntity = new TestEntity(addresses);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.addresses).isEqualTo(addresses);
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
@Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
List<CidrAddressBlock> addresses;
private TestEntity() {}
private TestEntity(List<CidrAddressBlock> addresses) {
this.addresses = addresses;
}
}
}

View File

@@ -0,0 +1,74 @@
// Copyright 2020 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.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.model.registrar.Registrar.BillingAccountEntry;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.joda.money.CurrencyUnit;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CurrencyToBillingMapUserType}. */
@RunWith(JUnit4.class)
public class CurrencyToBillingMapUserTypeTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder()
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
.withEntityClass(TestEntity.class)
.buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameCurrencyToBillingMap() {
ImmutableMap<CurrencyUnit, BillingAccountEntry> currencyToBilling =
ImmutableMap.of(
CurrencyUnit.of("USD"),
new BillingAccountEntry(CurrencyUnit.of("USD"), "accountId1"),
CurrencyUnit.of("CNY"),
new BillingAccountEntry(CurrencyUnit.of("CNY"), "accountId2"));
TestEntity testEntity = new TestEntity(currencyToBilling);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.currencyToBilling).containsExactlyEntriesIn(currencyToBilling);
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
@Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
Map<CurrencyUnit, BillingAccountEntry> currencyToBilling;
private TestEntity() {}
private TestEntity(Map<CurrencyUnit, BillingAccountEntry> currencyToBilling) {
this.currencyToBilling = currencyToBilling;
}
}
}

View File

@@ -56,8 +56,8 @@ public class EnumSetUserTypeTest {
public static class TestEnumType extends EnumSetUserType<TestEnum> {
@Override
protected Object convertToElem(Object value) {
return TestEnum.valueOf((String) value);
protected TestEnum convertToElem(String value) {
return TestEnum.valueOf(value);
}
}
}

View File

@@ -0,0 +1,77 @@
// Copyright 2020 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.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.model.ImmutableObject;
import google.registry.model.registrar.Registrar.State;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RegistrarStateConverter}. */
@RunWith(JUnit4.class)
public class RegistrarStateConverterTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameEnum() {
TestEntity testEntity = new TestEntity(State.ACTIVE);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.state).isEqualTo(State.ACTIVE);
}
@Test
public void testNativeQuery_succeeds() {
TestEntity testEntity = new TestEntity(State.DISABLED);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
assertThat(
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createNativeQuery("SELECT state FROM \"TestEntity\" WHERE name = 'id'")
.getSingleResult()))
.isEqualTo("DISABLED");
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
State state;
private TestEntity() {}
private TestEntity(State state) {
this.state = state;
}
}
}

View File

@@ -0,0 +1,76 @@
// Copyright 2020 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.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.model.ImmutableObject;
import google.registry.model.registrar.Registrar.Type;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RegistrarTypeConverter}. */
@RunWith(JUnit4.class)
public class RegistrarTypeConverterTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameEnum() {
TestEntity testEntity = new TestEntity(Type.MONITORING);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.type).isEqualTo(Type.MONITORING);
}
@Test
public void testNativeQuery_succeeds() {
TestEntity testEntity = new TestEntity(Type.MONITORING);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
assertThat(
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createNativeQuery("SELECT type FROM \"TestEntity\" WHERE name = 'id'")
.getSingleResult()))
.isEqualTo("MONITORING");
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
Type type;
private TestEntity() {}
private TestEntity(Type type) {
this.type = type;
}
}
}

View File

@@ -30,6 +30,7 @@ import google.registry.tools.UpdateReservedListCommandTest;
import google.registry.tools.server.CreatePremiumListActionTest;
import google.registry.tools.server.UpdatePremiumListActionTest;
import google.registry.ui.server.registrar.RegistryLockGetActionTest;
import google.registry.ui.server.registrar.RegistryLockVerifyActionTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -60,6 +61,7 @@ import org.junit.runners.Suite.SuiteClasses;
PremiumListDaoTest.class,
RegistryLockDaoTest.class,
RegistryLockGetActionTest.class,
RegistryLockVerifyActionTest.class,
ReservedListDaoTest.class,
UnlockDomainCommandTest.class,
UpdatePremiumListActionTest.class,

View File

@@ -202,4 +202,38 @@ public class PremiumListDaoTest {
() -> PremiumListDao.getPremiumPrice("foobar", Registry.get("tld")));
assertThat(thrown).hasMessageThat().isEqualTo("Could not load premium list 'tld'");
}
@Test
public void testGetPremiumPrice_worksForJPY() {
persistResource(
newRegistry("foobar", "FOOBAR")
.asBuilder()
.setPremiumListKey(
Key.create(
getCrossTldKey(),
google.registry.model.registry.label.PremiumList.class,
"premlist"))
.build());
PremiumListDao.saveNew(
PremiumList.create(
"premlist",
JPY,
ImmutableMap.of(
"silver",
BigDecimal.valueOf(10.00),
"gold",
BigDecimal.valueOf(1000.0),
"palladium",
BigDecimal.valueOf(15000))));
assertThat(PremiumListDao.getPremiumPrice("silver", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 10));
assertThat(PremiumListDao.getPremiumPrice("gold", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 1000));
assertThat(PremiumListDao.getPremiumPrice("palladium", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 15000));
}
private static Money moneyOf(CurrencyUnit unit, double amount) {
return Money.of(unit, BigDecimal.valueOf(amount).setScale(unit.getDecimalPlaces()));
}
}

View File

@@ -83,7 +83,8 @@ public final class RegistryTestServer {
route("/registrar-ote-setup", FrontendServlet.class),
route("/registrar-ote-status", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class),
route("/registry-lock-get", FrontendServlet.class));
route("/registry-lock-get", FrontendServlet.class),
route("/registry-lock-verify", FrontendServlet.class));
private static final ImmutableList<Class<? extends Filter>> FILTERS = ImmutableList.of(
ObjectifyFilter.class,

View File

@@ -0,0 +1,345 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.ui.server.registrar;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.common.collect.ImmutableMap;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.registry.Registry;
import google.registry.model.registry.RegistryLockDao;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.request.auth.AuthLevel;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.UserAuthInfo;
import google.registry.schema.domain.RegistryLock;
import google.registry.security.XsrfTokenManager;
import google.registry.testing.AppEngineRule;
import google.registry.testing.DatastoreHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectRule;
import google.registry.testing.UserInfo;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class RegistryLockVerifyActionTest {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final AppEngineRule appEngineRule =
AppEngineRule.builder()
.withDatastore()
.withUserService(UserInfo.create("marla.singer@example.com", "12345"))
.build();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Rule public final InjectRule inject = new InjectRule();
private final HttpServletRequest request = mock(HttpServletRequest.class);
private final UserService userService = UserServiceFactory.getUserService();
private final User user = new User("marla.singer@example.com", "gmail.com", "12345");
private final String lockId = "f1be78a2-2d61-458c-80f0-9dd8f2f8625f";
private FakeResponse response;
private DomainBase domain;
private AuthResult authResult;
private RegistryLockVerifyAction action;
@Before
public void setup() {
createTlds("tld", "net");
HostResource host = persistActiveHost("ns1.example.net");
domain = persistResource(newDomainBase("example.tld", host));
when(request.getRequestURI()).thenReturn("https://registry.example/registry-lock-verification");
action = createAction(lockId, true);
}
@Test
public void testSuccess_lockDomain() {
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(reloadDomain().getStatusValues()).containsExactlyElementsIn(REGISTRY_LOCK_STATUSES);
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
assertThat(historyEntry.getBySuperuser()).isFalse();
assertThat(historyEntry.getReason())
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
assertBillingEvent(historyEntry);
}
@Test
public void testSuccess_unlockDomain() {
action = createAction(lockId, false);
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
assertThat(reloadDomain().getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
assertThat(historyEntry.getBySuperuser()).isFalse();
assertThat(historyEntry.getReason())
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
assertBillingEvent(historyEntry);
}
@Test
public void testSuccess_adminLock_createsOnlyHistoryEntry() {
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true));
RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build());
action.run();
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isFalse();
assertThat(historyEntry.getBySuperuser()).isTrue();
DatastoreHelper.assertNoBillingEvents();
}
@Test
public void testFailure_badVerificationCode() {
RegistryLockDao.save(
createLock().asBuilder().setVerificationCode(UUID.randomUUID().toString()).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Invalid verification code");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyVerified() {
RegistryLockDao.save(
createLock().asBuilder().setLockCompletionTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
assertNoDomainChanges();
}
@Test
public void testFailure_expired() {
RegistryLockDao.save(createLock());
fakeClock.advanceBy(Duration.standardHours(2));
action.run();
assertThat(response.getPayload())
.contains("Failed: The pending lock has expired; please try again");
assertNoDomainChanges();
}
@Test
public void testFailure_nonAdmin_verifyingAdminLock() {
RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Non-admin user cannot complete admin lock");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyUnlocked() {
action = createAction(lockId, false);
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(fakeClock.nowUtc())
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.setUnlockCompletionTimestamp(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyLocked() {
RegistryLockDao.save(createLock());
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
assertNoDomainChanges();
}
@Test
public void testFailure_notLoggedIn() {
action.authResult = AuthResult.NOT_AUTHENTICATED;
action.run();
assertThat(response.getStatus()).isEqualTo(SC_MOVED_TEMPORARILY);
assertThat(response.getHeaders()).containsKey("Location");
assertNoDomainChanges();
}
@Test
public void testFailure_doesNotChangeLockObject() {
// A failure when performing Datastore actions means that no actions should be taken in the
// Cloud SQL RegistryLock object
RegistryLock lock = createLock();
RegistryLockDao.save(lock);
// reload the lock to pick up creation time
lock = RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
fakeClock.advanceOneMilli();
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
action.run();
// we would have failed during the Datastore segment of the action
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
// verify that the changes to the SQL object were rolled back
RegistryLock afterAction =
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
assertThat(afterAction).isEqualTo(lock);
}
@Test
public void testFailure_isLockTrue_shouldBeFalse() {
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(fakeClock.nowUtc())
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
}
@Test
public void testFailure_isLockFalse_shouldBeTrue() {
action = createAction(lockId, false);
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
}
@Test
public void testFailure_lock_unlock_lockAgain() {
RegistryLock lock = RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
String unlockVerificationCode = "some-unlock-code";
RegistryLockDao.save(
lock.asBuilder()
.setVerificationCode(unlockVerificationCode)
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.build());
action = createAction(unlockVerificationCode, false);
action.run();
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
action = createAction(lockId, true);
action.run();
assertThat(response.getPayload()).contains("Failed: Invalid verification code");
}
@Test
public void testFailure_lock_lockAgain() {
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
action = createAction(lockId, true);
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
}
@Test
public void testFailure_unlock_unlockAgain() {
action = createAction(lockId, false);
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
action = createAction(lockId, false);
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
}
private RegistryLock createLock() {
return new RegistryLock.Builder()
.setDomainName("example.tld")
.setRegistrarId("TheRegistrar")
.setRepoId("repoId")
.setRegistrarPocId("marla.singer@example.com")
.isSuperuser(false)
.setVerificationCode(lockId)
.build();
}
private DomainBase reloadDomain() {
return ofy().load().entity(domain).now();
}
private void assertNoDomainChanges() {
assertThat(reloadDomain()).isEqualTo(domain);
}
private void assertBillingEvent(HistoryEntry historyEntry) {
DatastoreHelper.assertBillingEvents(
new BillingEvent.OneTime.Builder()
.setReason(Reason.SERVER_STATUS)
.setTargetId(domain.getForeignKey())
.setClientId(domain.getCurrentSponsorClientId())
.setCost(Registry.get(domain.getTld()).getServerStatusChangeCost())
.setEventTime(fakeClock.nowUtc())
.setBillingTime(fakeClock.nowUtc())
.setParent(historyEntry)
.build());
}
private RegistryLockVerifyAction createAction(String lockVerificationCode, Boolean isLock) {
response = new FakeResponse();
RegistryLockVerifyAction action =
new RegistryLockVerifyAction(fakeClock, lockVerificationCode, isLock);
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
action.req = request;
action.response = response;
action.authResult = authResult;
action.userService = userService;
action.logoFilename = "logo.png";
action.productName = "Nomulus";
action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId");
action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService);
return action;
}
}

View File

@@ -18,7 +18,9 @@ import static google.registry.server.Fixture.BASIC;
import static google.registry.server.Route.route;
import static google.registry.testing.AppEngineRule.makeRegistrar2;
import static google.registry.testing.AppEngineRule.makeRegistrarContact2;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
@@ -26,7 +28,9 @@ import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.ObjectifyFilter;
import google.registry.model.ofy.OfyFilter;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registry.RegistryLockDao;
import google.registry.module.frontend.FrontendServlet;
import google.registry.schema.domain.RegistryLock;
import google.registry.server.RegistryTestServer;
import google.registry.testing.CertificateSamples;
import org.junit.Rule;
@@ -46,7 +50,8 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
.setRoutes(
route("/registrar", FrontendServlet.class),
route("/registrar-ote-status", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class))
route("/registrar-settings", FrontendServlet.class),
route("/registry-lock-verify", FrontendServlet.class))
.setFilters(ObjectifyFilter.class, OfyFilter.class)
.setFixtures(BASIC)
.setEmail("Marla.Singer@google.com")
@@ -370,4 +375,36 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
Thread.sleep(500);
driver.diffPage("page");
}
@Test
public void registryLockVerify_success() throws Throwable {
String lockVerificationCode = "f1be78a2-2d61-458c-80f0-9dd8f2f8625f";
server.runInAppEngineEnvironment(
() -> {
createTld("tld");
persistResource(newDomainBase("example.tld"));
RegistryLockDao.save(
new RegistryLock.Builder()
.setRegistrarPocId("johndoe@theregistrar.com")
.setRepoId("repoId")
.setRegistrarId("TheRegistrar")
.setVerificationCode("f1be78a2-2d61-458c-80f0-9dd8f2f8625f")
.isSuperuser(false)
.setDomainName("example.tld")
.build());
return null;
});
driver.get(
server.getUrl(
"/registry-lock-verify?isLock=true&lockVerificationCode=" + lockVerificationCode));
driver.waitForElement(By.id("reg-content"));
driver.diffPage("page");
}
@Test
public void registryLockVerify_unknownLock() throws Throwable {
driver.get(server.getUrl("/registry-lock-verify?isLock=true&lockVerificationCode=asdfasdf"));
driver.waitForElement(By.id("reg-content"));
driver.diffPage("page");
}
}

View File

@@ -6,3 +6,4 @@ PATH CLASS METHODS OK AUTH_METHODS
/registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
/registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY USER PUBLIC

View File

@@ -0,0 +1,65 @@
-- Copyright 2020 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 table "Registrar" (
client_id text not null,
allowed_tlds text[],
billing_account_map hstore,
billing_identifier int8,
block_premium_names boolean not null,
client_certificate text,
client_certificate_hash text,
contacts_require_syncing boolean not null,
creation_time timestamptz,
drive_folder_id text,
email_address text,
failover_client_certificate text,
failover_client_certificate_hash text,
fax_number text,
iana_identifier int8,
icann_referral_email text,
i18n_address_city text,
i18n_address_country_code text,
i18n_address_state text,
i18n_address_street_line1 text,
i18n_address_street_line2 text,
i18n_address_street_line3 text,
i18n_address_zip text,
ip_address_whitelist text[],
last_certificate_update_time timestamptz,
last_update_time timestamptz,
localized_address_city text,
localized_address_country_code text,
localized_address_state text,
localized_address_street_line1 text,
localized_address_street_line2 text,
localized_address_street_line3 text,
localized_address_zip text,
password_hash text,
phone_number text,
phone_passcode text,
po_number text,
rdap_base_urls text[],
registrar_name text not null,
registry_lock_allowed boolean not null,
password_salt text,
state text,
type text not null,
url text,
whois_server text,
primary key (client_id)
);
create index registrar_name_idx on "Registrar" (registrar_name);
create index registrar_iana_identifier_idx on "Registrar" (iana_identifier);

View File

@@ -93,6 +93,55 @@
primary key (revision_id)
);
create table "Registrar" (
client_id text not null,
allowed_tlds text[],
billing_account_map hstore,
billing_identifier int8,
block_premium_names boolean not null,
client_certificate text,
client_certificate_hash text,
contacts_require_syncing boolean not null,
creation_time timestamptz,
drive_folder_id text,
email_address text,
failover_client_certificate text,
failover_client_certificate_hash text,
fax_number text,
iana_identifier int8,
icann_referral_email text,
i18n_address_city text,
i18n_address_country_code text,
i18n_address_state text,
i18n_address_street_line1 text,
i18n_address_street_line2 text,
i18n_address_street_line3 text,
i18n_address_zip text,
ip_address_whitelist text[],
last_certificate_update_time timestamptz,
last_update_time timestamptz,
localized_address_city text,
localized_address_country_code text,
localized_address_state text,
localized_address_street_line1 text,
localized_address_street_line2 text,
localized_address_street_line3 text,
localized_address_zip text,
password_hash text,
phone_number text,
phone_passcode text,
po_number text,
rdap_base_urls text[],
registrar_name text not null,
registry_lock_allowed boolean not null,
password_salt text,
state text,
type text not null,
url text,
whois_server text,
primary key (client_id)
);
create table "RegistryLock" (
revision_id bigserial not null,
domain_name text not null,
@@ -130,6 +179,8 @@ create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
create index IDX1rcgkdd777bpvj0r94sltwd5y on "Domain" (fully_qualified_domain_name);
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
create index premiumlist_name_idx on "PremiumList" (name);
create index registrar_name_idx on "Registrar" (registrar_name);
create index registrar_iana_identifier_idx on "Registrar" (iana_identifier);
create index idx_registry_lock_verification_code on "RegistryLock" (verification_code);
create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);

View File

@@ -159,6 +159,59 @@ CREATE SEQUENCE public."PremiumList_revision_id_seq"
ALTER SEQUENCE public."PremiumList_revision_id_seq" OWNED BY public."PremiumList".revision_id;
--
-- Name: Registrar; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."Registrar" (
client_id text NOT NULL,
allowed_tlds text[],
billing_account_map public.hstore,
billing_identifier bigint,
block_premium_names boolean NOT NULL,
client_certificate text,
client_certificate_hash text,
contacts_require_syncing boolean NOT NULL,
creation_time timestamp with time zone,
drive_folder_id text,
email_address text,
failover_client_certificate text,
failover_client_certificate_hash text,
fax_number text,
iana_identifier bigint,
icann_referral_email text,
i18n_address_city text,
i18n_address_country_code text,
i18n_address_state text,
i18n_address_street_line1 text,
i18n_address_street_line2 text,
i18n_address_street_line3 text,
i18n_address_zip text,
ip_address_whitelist text[],
last_certificate_update_time timestamp with time zone,
last_update_time timestamp with time zone,
localized_address_city text,
localized_address_country_code text,
localized_address_state text,
localized_address_street_line1 text,
localized_address_street_line2 text,
localized_address_street_line3 text,
localized_address_zip text,
password_hash text,
phone_number text,
phone_passcode text,
po_number text,
rdap_base_urls text[],
registrar_name text NOT NULL,
registry_lock_allowed boolean NOT NULL,
password_salt text,
state text,
type text NOT NULL,
url text,
whois_server text
);
--
-- Name: RegistryLock; Type: TABLE; Schema: public; Owner: -
--
@@ -317,6 +370,14 @@ ALTER TABLE ONLY public."PremiumList"
ADD CONSTRAINT "PremiumList_pkey" PRIMARY KEY (revision_id);
--
-- Name: Registrar Registrar_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Registrar"
ADD CONSTRAINT "Registrar_pkey" PRIMARY KEY (client_id);
--
-- Name: RegistryLock RegistryLock_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -405,6 +466,20 @@ CREATE INDEX idxrwl38wwkli1j7gkvtywi9jokq ON public."Domain" USING btree (tld);
CREATE INDEX premiumlist_name_idx ON public."PremiumList" USING btree (name);
--
-- Name: registrar_iana_identifier_idx; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX registrar_iana_identifier_idx ON public."Registrar" USING btree (iana_identifier);
--
-- Name: registrar_name_idx; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX registrar_name_idx ON public."Registrar" USING btree (registrar_name);
--
-- Name: reservedlist_name_idx; Type: INDEX; Schema: public; Owner: -
--

View File

@@ -12,16 +12,23 @@
#
# To trigger a build automatically, follow the instructions below and add a trigger:
# https://cloud.google.com/cloud-build/docs/running-builds/automate-builds
#
# Note: to work around issue in Spinnaker's 'Deployment Manifest' stage,
# variable references must avoid the ${var} format. Valid formats include
# $var or ${"${var}"}. This file use the former. Since TAG_NAME and _ENV are
# expanded in the copies sent to Spinnaker, we preserve the brackets around
# them for safe pattern matching during release.
# See https://github.com/spinnaker/spinnaker/issues/3028 for more information.
steps:
# Pull the credential for nomulus tool.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
args:
- gsutil
- cp
- gs://${PROJECT_ID}-deploy/secrets/tool-credential.json.enc
- gs://$PROJECT_ID-deploy/secrets/tool-credential.json.enc
- .
# Decrypt the credential.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
@@ -31,7 +38,7 @@ steps:
--ciphertext-file=- --plaintext-file=tool-credential.json \
--location=global --keyring=nomulus-tool-keyring --key=nomulus-tool-key
# Deploy the Spec11 pipeline to GCS.
- name: 'gcr.io/${PROJECT_ID}/nomulus-tool:latest'
- name: 'gcr.io/$PROJECT_ID/nomulus-tool:latest'
args:
- -e
- ${_ENV}
@@ -39,7 +46,7 @@ steps:
- tool-credential.json
- deploy_spec11_pipeline
# Deploy the invoicing pipeline to GCS.
- name: 'gcr.io/${PROJECT_ID}/nomulus-tool:latest'
- name: 'gcr.io/$PROJECT_ID/nomulus-tool:latest'
args:
- -e
- ${_ENV}
@@ -49,18 +56,18 @@ steps:
# Save the deployed tag for the current environment on GCS. Because of b/137891685
# which causes the for-loop in the next step to fail, this may not be the last step.
# TODO(weiminyu): do this in last step.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
set -e
echo ${TAG_NAME} | \
gsutil cp - gs://${PROJECT_ID}-deployed-tags/nomulus.${_ENV}.tag
gsutil cp - gs://$PROJECT_ID-deployed-tags/nomulus.${_ENV}.tag
# Deploy the GAE config files.
# First authorize the gcloud tool to use the credential json file, then
# download and unzip the tarball that contains the relevant config files
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
@@ -72,13 +79,13 @@ steps:
else
project_id="domain-registry-${_ENV}"
fi
gsutil cp gs://${PROJECT_ID}-deploy/${TAG_NAME}/${_ENV}.tar .
gsutil cp gs://$PROJECT_ID-deploy/${TAG_NAME}/${_ENV}.tar .
tar -xvf ${_ENV}.tar
# Note that this currently does not work for google.com projects that
# we use due to b/137891685. External projects are likely to work.
for filename in cron dispatch dos index queue; do
gcloud -q --project ${project_id} app deploy \
default/WEB-INF/appengine-generated/${filename}.yaml
gcloud -q --project $project_id app deploy \
default/WEB-INF/appengine-generated/$filename.yaml
done
timeout: 3600s

View File

@@ -15,14 +15,22 @@
#
# Note that the release process hardens the tags and variables in this file:
# - The 'latest' tag on docker images will be replaced by their image digests.
# - The ${TAG_NAME} pattern will be replaced by the acutal release tag.
# - The ${TAG_NAME} pattern will be replaced by the actual release tag.
# - The ${_ENV} pattern will be replaced by the actual environment name.
# Please refer to ./cloudbuild-release.yaml for more details.
# Note 2: to work around issue in Spinnaker's 'Deployment Manifest' stage,
# variable references must avoid the ${var} format. Valid formats include
# $var or ${"${var}"}. This file use the former. Since TAG_NAME and _ENV are
# expanded in the copies sent to Spinnaker, we preserve the brackets around
# them for safe pattern matching during release.
# See https://github.com/spinnaker/spinnaker/issues/3028 for more information.
steps:
# Download and decrypt the nomulus tool credential, which has the privilege to
# start Cloud SQL proxy to all environments.
# Also download and decrypt the admin_credential file, which has the cloud
# instance name and database login name and password.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
volumes:
- name: 'secrets'
path: '/secrets'
@@ -31,13 +39,13 @@ steps:
- -c
- |
set -e
gsutil cp gs://${PROJECT_ID}-deploy/secrets/tool-credential.json.enc - \
gsutil cp gs://$PROJECT_ID-deploy/secrets/tool-credential.json.enc - \
| base64 -d \
| gcloud kms decrypt \
--ciphertext-file=- \
--plaintext-file=/secrets/cloud_sql_credential.json \
--location=global --keyring=nomulus-tool-keyring --key=nomulus-tool-key
gsutil cp gs://${PROJECT_ID}-deploy/cloudsql-credentials/${_ENV}/admin_credential.enc - \
gsutil cp gs://$PROJECT_ID-deploy/cloudsql-credentials/${_ENV}/admin_credential.enc - \
| base64 -d \
| gcloud kms decrypt \
--ciphertext-file=- \
@@ -45,7 +53,7 @@ steps:
--location global --keyring=nomulus-tool-keyring \
--key=nomulus-tool-key
# Download the schema jar to be deployed.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
volumes:
- name: 'flyway'
path: '/flyway/jars'
@@ -54,10 +62,10 @@ steps:
- -c
- |
set -e
gsutil cp gs://domain-registry-dev-deploy/${TAG_NAME}/schema.jar \
gsutil cp gs://$PROJECT_ID-deploy/${TAG_NAME}/schema.jar \
/flyway/jars
# Deploy SQL schema
- name: 'gcr.io/${PROJECT_ID}/schema_deployer:latest'
- name: 'gcr.io/$PROJECT_ID/schema_deployer:latest'
volumes:
- name: 'secrets'
path: '/secrets'
@@ -68,14 +76,14 @@ steps:
# location. Do not use the 'artifacts' section for this since it will
# upload an extra metadata file every time and pollute the folder.
# TODO(weiminyu): modify this step so that TAG_NAME may be 'live'.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
entrypoint: /bin/bash
args:
- -c
- |
set -e
echo ${TAG_NAME} | \
gsutil cp - gs://${PROJECT_ID}-deployed-tags/sql.${_ENV}.tag\
gsutil cp - gs://$PROJECT_ID-deployed-tags/sql.${_ENV}.tag\
timeout: 3600s
options:
machineType: 'N1_HIGHCPU_8'

View File

@@ -9,16 +9,23 @@
#
# To trigger a build automatically, follow the instructions below and add a trigger:
# https://cloud.google.com/cloud-build/docs/running-builds/automate-builds
#
# Note: to work around issue in Spinnaker's 'Deployment Manifest' stage,
# variable references must avoid the ${var} format. Valid formats include
# $var or ${"${var}"}. This file use the former. Since TAG_NAME is
# expanded in the copies sent to Spinnaker, we preserve the brackets around
# them for safe pattern matching during release.
# See https://github.com/spinnaker/spinnaker/issues/3028 for more information.
steps:
# Rsync the folder.
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
args:
- gsutil
- -m
- rsync
- -d
- gs://${PROJECT_ID}-deploy/${TAG_NAME}
- gs://${PROJECT_ID}-deploy/live
- gs://$PROJECT_ID-deploy/${TAG_NAME}
- gs://$PROJECT_ID-deploy/live
timeout: 3600s
options:
machineType: 'N1_HIGHCPU_8'

View File

@@ -11,15 +11,22 @@
#
# To trigger a build automatically, follow the instructions below and add a trigger:
# https://cloud.google.com/cloud-build/docs/running-builds/automate-builds
#
# Note: to work around issue in Spinnaker's 'Deployment Manifest' stage,
# variable references must avoid the ${var} format. Valid formats include
# $var or ${"${var}"}. This file use the former. Since TAG_NAME is
# expanded in the copies sent to Spinnaker, we preserve the brackets around
# them for safe pattern matching during release.
# See https://github.com/spinnaker/spinnaker/issues/3028 for more information.
steps:
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
- name: 'gcr.io/$PROJECT_ID/builder:latest'
args:
- gcloud
- container
- images
- add-tag
- gcr.io/${PROJECT_ID}/${_IMAGE}:${TAG_NAME}
- gcr.io/${PROJECT_ID}/${_IMAGE}:live
- gcr.io/$PROJECT_ID/${_IMAGE}:${TAG_NAME}
- gcr.io/$PROJECT_ID/${_IMAGE}:live
timeout: 3600s
options:
machineType: 'N1_HIGHCPU_8'