mirror of
https://github.com/google/nomulus
synced 2026-07-10 18:13:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62433c2238 | |||
| 90945bcc30 | |||
| f134c4bf37 | |||
| 44921c29d6 | |||
| ce80278ab7 | |||
| 594ce30122 | |||
| 736f788eea |
@@ -97,6 +97,7 @@ nomulus.iws
|
||||
!/gradle/wrapper/**/*.jar
|
||||
.gradle/
|
||||
**/build
|
||||
cloudbuild-caches/
|
||||
node_modules/**
|
||||
/repos/
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import sys
|
||||
import re
|
||||
|
||||
# We should never analyze any generated files
|
||||
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "/out/"}
|
||||
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/"}
|
||||
# We can't rely on CI to have the Enum package installed so we do this instead.
|
||||
FORBIDDEN = 1
|
||||
REQUIRED = 2
|
||||
|
||||
@@ -53,7 +53,6 @@ import java.util.concurrent.ExecutionException;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Transient;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -117,7 +116,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
DateTime lastEppUpdateTime;
|
||||
|
||||
/** Status values associated with this resource. */
|
||||
@Type(type = "google.registry.model.eppcommon.StatusValue$StatusValueSetType")
|
||||
@Column(name = "statuses")
|
||||
// TODO(mmuller): rename to "statuses" once we're off datastore.
|
||||
Set<StatusValue> status;
|
||||
|
||||
@@ -185,7 +185,6 @@ public class DomainBase extends EppResource
|
||||
String idnTableName;
|
||||
|
||||
/** Fully qualified host names of this domain's active subordinate hosts. */
|
||||
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
|
||||
Set<String> subordinateHosts;
|
||||
|
||||
/** When this domain's registration will expire. */
|
||||
|
||||
@@ -25,7 +25,6 @@ import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
|
||||
import google.registry.model.translators.StatusValueAdapter;
|
||||
import google.registry.persistence.EnumSetUserType;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
/**
|
||||
@@ -166,6 +165,4 @@ public enum StatusValue implements EppEnum {
|
||||
return StatusValue.valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, nullToEmpty(xmlName)));
|
||||
}
|
||||
|
||||
/** Hibernate type for sets of {@link StatusValue}. */
|
||||
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {}
|
||||
}
|
||||
|
||||
@@ -262,15 +262,12 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -298,7 +295,6 @@ 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. */
|
||||
|
||||
@@ -42,14 +42,12 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.persistence.EnumSetUserType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
|
||||
@@ -103,9 +101,6 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
this.displayName = display;
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
/** Hibernate type for sets of {@link Type}. */
|
||||
public static class RegistrarPocType extends EnumSetUserType<Type> {}
|
||||
}
|
||||
|
||||
/** The name of the contact. */
|
||||
@@ -127,8 +122,6 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
* Multiple types are used to associate the registrar contact with various mailing groups. This
|
||||
* data is internal to the registry.
|
||||
*/
|
||||
@org.hibernate.annotations.Type(
|
||||
type = "google.registry.model.registrar.RegistrarContact$Type$RegistrarPocType")
|
||||
Set<Type> types;
|
||||
|
||||
/**
|
||||
|
||||
+11
-7
@@ -16,20 +16,24 @@ package google.registry.persistence;
|
||||
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.util.List;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/**
|
||||
* Hibernate {@link org.hibernate.usertype.UserType} for storing/retrieving {@link
|
||||
* List<CidrAddressBlock>} objects.
|
||||
* JPA {@link AttributeConverter} for storing/retrieving {@link List<CidrAddressBlock>} objects.
|
||||
* TODO(shicong): Investigate if we can have one converter for any List type
|
||||
*/
|
||||
public class CidrAddressBlockListUserType extends StringListUserType<CidrAddressBlock> {
|
||||
@Converter(autoApply = true)
|
||||
public class CidrAddressBlockListConverter extends StringListConverterBase<CidrAddressBlock> {
|
||||
|
||||
@Override
|
||||
protected CidrAddressBlock convertToElem(String columnValue) {
|
||||
return columnValue == null ? null : CidrAddressBlock.create(columnValue);
|
||||
String toString(CidrAddressBlock element) {
|
||||
return element.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertToColumn(CidrAddressBlock elementValue) {
|
||||
return elementValue == null ? null : elementValue.toString();
|
||||
CidrAddressBlock fromString(String value) {
|
||||
return CidrAddressBlock.create(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// 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 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<E>, E, String> {
|
||||
|
||||
@Override
|
||||
Set<E> getNewCollection() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
ArrayColumnType getColumnType() {
|
||||
return ArrayColumnType.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class returnedClass() {
|
||||
return Set.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E convertToElem(String columnValue) {
|
||||
return columnValue == null
|
||||
? null
|
||||
: Enum.valueOf(new TypeInstantiator<E>(getClass()) {}.getExactType(), columnValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertToColumn(E elementValue) {
|
||||
return elementValue == null ? null : elementValue.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// 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 java.sql.Array;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
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.
|
||||
*
|
||||
* @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();
|
||||
|
||||
abstract ArrayColumnType getColumnType();
|
||||
|
||||
enum ArrayColumnType {
|
||||
STRING(Types.ARRAY, "text");
|
||||
|
||||
final int typeCode;
|
||||
final String typeName;
|
||||
|
||||
ArrayColumnType(int typeCode, String typeName) {
|
||||
this.typeCode = typeCode;
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
int getTypeCode() {
|
||||
return typeCode;
|
||||
}
|
||||
|
||||
String getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
String getTypeDdlName() {
|
||||
return typeName + "[]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class returnedClass() {
|
||||
return new TypeInstantiator<T>(getClass()) {}.getExactType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] sqlTypes() {
|
||||
return new int[] {getColumnType().getTypeCode()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullSafeGet(
|
||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException, SQLException {
|
||||
if (rs.getArray(names[0]) != null) {
|
||||
T result = getNewCollection();
|
||||
for (C element : (C[]) rs.getArray(names[0]).getArray()) {
|
||||
result.add(convertToElem(element));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeSet(
|
||||
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
||||
throws HibernateException, SQLException {
|
||||
if (value == null) {
|
||||
st.setArray(index, null);
|
||||
return;
|
||||
}
|
||||
T collection = (T) value;
|
||||
Array arr =
|
||||
st.getConnection()
|
||||
.createArrayOf(
|
||||
getColumnType().getTypeName(),
|
||||
collection.stream().map(this::convertToColumn).toArray());
|
||||
st.setArray(index, arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to convert an element value retrieved from the database to a different type.
|
||||
*
|
||||
* <p>This method is useful when encoding a java type to one of the types that can be used as an
|
||||
* array element.
|
||||
*/
|
||||
protected E convertToElem(C columnValue) {
|
||||
return (E) columnValue;
|
||||
}
|
||||
|
||||
protected C convertToColumn(E elementValue) {
|
||||
return (C) elementValue;
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,10 @@
|
||||
// limitations under the License.
|
||||
package google.registry.persistence;
|
||||
|
||||
import google.registry.persistence.GenericCollectionUserType.ArrayColumnType;
|
||||
import java.sql.Types;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.PostgreSQL95Dialect;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
|
||||
/** Nomulus mapping rules for column types in Postgresql. */
|
||||
public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||
@@ -25,8 +26,15 @@ public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
|
||||
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz");
|
||||
registerColumnType(Types.TIMESTAMP, "timestamptz");
|
||||
registerColumnType(Types.OTHER, "hstore");
|
||||
for (ArrayColumnType arrayType : ArrayColumnType.values()) {
|
||||
registerColumnType(arrayType.getTypeCode(), arrayType.getTypeDdlName());
|
||||
}
|
||||
registerColumnType(
|
||||
StringCollectionDescriptor.COLUMN_TYPE, StringCollectionDescriptor.COLUMN_DDL_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(
|
||||
TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes(typeContributions, serviceRegistry);
|
||||
typeContributions.contributeJavaTypeDescriptor(new StringCollectionDescriptor());
|
||||
typeContributions.contributeSqlTypeDescriptor(new StringCollectionDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.model.registrar.RegistrarContact.Type;
|
||||
import java.util.Set;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<Type>}. */
|
||||
@Converter(autoApply = true)
|
||||
public class RegistrarPocSetConverter extends StringSetConverterBase<Type> {
|
||||
@Override
|
||||
String toString(Type element) {
|
||||
return element.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
Type fromString(String value) {
|
||||
return Type.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -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.model.eppcommon.StatusValue;
|
||||
import java.util.Set;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<StatusValue>}. */
|
||||
@Converter(autoApply = true)
|
||||
public class StatusValueSetConverter extends StringSetConverterBase<StatusValue> {
|
||||
|
||||
@Override
|
||||
String toString(StatusValue element) {
|
||||
return element.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
StatusValue fromString(String value) {
|
||||
return StatusValue.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// 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 google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.sql.Array;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Collection;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
|
||||
import org.hibernate.type.descriptor.sql.BasicBinder;
|
||||
import org.hibernate.type.descriptor.sql.BasicExtractor;
|
||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||
|
||||
/**
|
||||
* The {@link JavaTypeDescriptor} and {@link SqlTypeDescriptor} for {@link StringCollection}.
|
||||
*
|
||||
* <p>A {@link StringCollection} object is a simple wrapper for a {@link Collection<String>} which
|
||||
* can be stored as a string array in the database. The {@link JavaTypeDescriptor} and {@link
|
||||
* SqlTypeDescriptor} is used by JPA/Hibernate to map between the collection and {@link Array} which
|
||||
* is the actual type that JDBC uses to read from and write to the database.
|
||||
*
|
||||
* @see <a
|
||||
* href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#basic-jpa-convert">JPA
|
||||
* 2.1 AttributeConverters</a>
|
||||
*/
|
||||
public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCollection>
|
||||
implements SqlTypeDescriptor {
|
||||
public static final int COLUMN_TYPE = Types.ARRAY;
|
||||
public static final String COLUMN_NAME = "text";
|
||||
public static final String COLUMN_DDL_NAME = COLUMN_NAME + "[]";
|
||||
|
||||
protected StringCollectionDescriptor() {
|
||||
super(StringCollection.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringCollection fromString(String string) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Constructing StringCollectionDescriptor from string is not allowed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(StringCollection value, Class<X> type, WrapperOptions options) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (Collection.class.isAssignableFrom(type)) {
|
||||
return (X) value.getCollection();
|
||||
}
|
||||
throw unknownUnwrap(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> StringCollection wrap(X value, WrapperOptions options) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
try {
|
||||
String[] arr = (String[]) ((Array) value).getArray();
|
||||
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
|
||||
for (String str : arr) {
|
||||
builder.add(str);
|
||||
}
|
||||
return StringCollection.create(builder.build());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
throw unknownWrap(value.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSqlType() {
|
||||
return COLUMN_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeRemapped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||
return new BasicBinder<X>(javaTypeDescriptor, this) {
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
if (value == null) {
|
||||
st.setArray(index, null);
|
||||
return;
|
||||
}
|
||||
if (value instanceof StringCollection) {
|
||||
StringCollection stringCollection = (StringCollection) value;
|
||||
if (stringCollection.getCollection() == null) {
|
||||
st.setArray(index, null);
|
||||
} else {
|
||||
st.setArray(
|
||||
index,
|
||||
st.getConnection()
|
||||
.createArrayOf(COLUMN_NAME, stringCollection.getCollection().toArray()));
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
String.format(
|
||||
"Binding type %s is not supported by StringCollectionDescriptor",
|
||||
value.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
// CallableStatement.setArray() doesn't have an overload version for setting array by its
|
||||
// column name
|
||||
throw new UnsupportedOperationException(
|
||||
"Binding array by its column name is not supported");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
|
||||
return new BasicExtractor<X>(javaTypeDescriptor, this) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
|
||||
return javaTypeDescriptor.wrap(rs.getArray(name), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return javaTypeDescriptor.wrap(statement.getArray(index), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return javaTypeDescriptor.wrap(statement.getArray(name), options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class StringCollection {
|
||||
private Collection<String> collection;
|
||||
|
||||
private StringCollection(Collection<String> collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public static StringCollection create(Collection<String> collection) {
|
||||
return new StringCollection(collection);
|
||||
}
|
||||
|
||||
public Collection<String> getCollection() {
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -14,19 +14,21 @@
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
|
||||
public class StringListUserType<E> extends GenericCollectionUserType<List<E>, E, String> {
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link List<String>}. */
|
||||
@Converter(autoApply = true)
|
||||
public class StringListConverter extends StringListConverterBase<String> {
|
||||
|
||||
@Override
|
||||
List<E> getNewCollection() {
|
||||
return Lists.newArrayList();
|
||||
String toString(String element) {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
ArrayColumnType getColumnType() {
|
||||
return ArrayColumnType.STRING;
|
||||
String fromString(String value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -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.ImmutableList.toImmutableList;
|
||||
|
||||
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||
import java.util.List;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Base JPA converter for {@link List} objects that are stored as an array of strings in the
|
||||
* database.
|
||||
*/
|
||||
public abstract class StringListConverterBase<T>
|
||||
implements AttributeConverter<List<T>, StringCollection> {
|
||||
|
||||
abstract String toString(T element);
|
||||
|
||||
abstract T fromString(String value);
|
||||
|
||||
@Override
|
||||
public StringCollection convertToDatabaseColumn(List<T> attribute) {
|
||||
return attribute == null
|
||||
? null
|
||||
: StringCollection.create(
|
||||
attribute.stream().map(this::toString).collect(toImmutableList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> convertToEntityAttribute(StringCollection dbData) {
|
||||
return dbData == null || dbData.getCollection() == null
|
||||
? null
|
||||
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableList());
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -14,19 +14,21 @@
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Set;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
|
||||
public class StringSetUserType<E> extends GenericCollectionUserType<Set<E>, E, String> {
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<String>}. */
|
||||
@Converter(autoApply = true)
|
||||
public class StringSetConverter extends StringSetConverterBase<String> {
|
||||
|
||||
@Override
|
||||
Set<E> getNewCollection() {
|
||||
return Sets.newHashSet();
|
||||
String toString(String element) {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
ArrayColumnType getColumnType() {
|
||||
return ArrayColumnType.STRING;
|
||||
String fromString(String value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.ImmutableSet.toImmutableSet;
|
||||
|
||||
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
|
||||
import java.util.Set;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Base JPA converter for {@link Set} objects that are stored as an array of strings in the
|
||||
* database.
|
||||
*/
|
||||
public abstract class StringSetConverterBase<T>
|
||||
implements AttributeConverter<Set<T>, StringCollection> {
|
||||
|
||||
abstract String toString(T element);
|
||||
|
||||
abstract T fromString(String value);
|
||||
|
||||
@Override
|
||||
public StringCollection convertToDatabaseColumn(Set<T> attribute) {
|
||||
return attribute == null
|
||||
? null
|
||||
: StringCollection.create(attribute.stream().map(this::toString).collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> convertToEntityAttribute(StringCollection dbData) {
|
||||
return dbData == null || dbData.getCollection() == null
|
||||
? null
|
||||
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
+28
-29
@@ -21,7 +21,6 @@ import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
import static google.registry.ui.server.registrar.RegistrarConsoleModule.PARAM_CLIENT_ID;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
@@ -34,6 +33,7 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.HttpException.ForbiddenException;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
@@ -123,10 +123,16 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
!Strings.isNullOrEmpty(postInput.fullyQualifiedDomainName),
|
||||
"Missing key for fullyQualifiedDomainName");
|
||||
checkNotNull(postInput.isLock, "Missing key for isLock");
|
||||
checkArgumentPresent(authResult.userAuthInfo(), "User is not logged in");
|
||||
UserAuthInfo userAuthInfo =
|
||||
authResult
|
||||
.userAuthInfo()
|
||||
.orElseThrow(() -> new ForbiddenException("User is not logged in"));
|
||||
|
||||
boolean isAdmin = authResult.userAuthInfo().get().isUserAdmin();
|
||||
verifyRegistryLockPassword(postInput);
|
||||
boolean isAdmin = userAuthInfo.isUserAdmin();
|
||||
String userEmail = userAuthInfo.user().getEmail();
|
||||
if (!isAdmin) {
|
||||
verifyRegistryLockPassword(postInput, userEmail);
|
||||
}
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
@@ -135,12 +141,12 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
? domainLockUtils.createRegistryLockRequest(
|
||||
postInput.fullyQualifiedDomainName,
|
||||
postInput.clientId,
|
||||
postInput.pocId,
|
||||
userEmail,
|
||||
isAdmin,
|
||||
clock)
|
||||
: domainLockUtils.createRegistryUnlockRequest(
|
||||
postInput.fullyQualifiedDomainName, postInput.clientId, isAdmin, clock);
|
||||
sendVerificationEmail(registryLock, postInput.isLock);
|
||||
sendVerificationEmail(registryLock, userEmail, postInput.isLock);
|
||||
});
|
||||
String action = postInput.isLock ? "lock" : "unlock";
|
||||
return JsonResponseHelper.create(SUCCESS, String.format("Successful %s", action));
|
||||
@@ -152,7 +158,7 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
}
|
||||
}
|
||||
|
||||
private void sendVerificationEmail(RegistryLock lock, boolean isLock) {
|
||||
private void sendVerificationEmail(RegistryLock lock, String userEmail, boolean isLock) {
|
||||
try {
|
||||
String url =
|
||||
new URIBuilder()
|
||||
@@ -165,8 +171,7 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
.toString();
|
||||
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, lock.getDomainName(), url);
|
||||
ImmutableList<InternetAddress> recipients =
|
||||
ImmutableList.of(
|
||||
new InternetAddress(authResult.userAuthInfo().get().user().getEmail(), true));
|
||||
ImmutableList.of(new InternetAddress(userEmail, true));
|
||||
String action = isLock ? "lock" : "unlock";
|
||||
sendEmailService.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
@@ -180,30 +185,25 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyRegistryLockPassword(RegistryLockPostInput postInput)
|
||||
private void verifyRegistryLockPassword(RegistryLockPostInput postInput, String userEmail)
|
||||
throws RegistrarAccessDeniedException {
|
||||
// Verify that the user can access the registrar and that the user is either an admin or has
|
||||
// Verify that the user can access the registrar and that the user has
|
||||
// registry lock enabled and provided a correct password
|
||||
checkArgument(authResult.userAuthInfo().isPresent(), "Auth result not present");
|
||||
Registrar registrar = registrarAccessor.getRegistrar(postInput.clientId);
|
||||
checkArgument(
|
||||
registrar.isRegistryLockAllowed(), "Registry lock not allowed for this registrar");
|
||||
UserAuthInfo userAuthInfo = authResult.userAuthInfo().get();
|
||||
if (!userAuthInfo.isUserAdmin()) {
|
||||
checkArgument(!Strings.isNullOrEmpty(postInput.pocId), "Missing key for pocId");
|
||||
checkArgument(!Strings.isNullOrEmpty(postInput.password), "Missing key for password");
|
||||
RegistrarContact registrarContact =
|
||||
registrar.getContacts().stream()
|
||||
.filter(contact -> contact.getEmailAddress().equals(postInput.pocId))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Unknown registrar POC ID %s", postInput.pocId)));
|
||||
checkArgument(
|
||||
registrarContact.verifyRegistryLockPassword(postInput.password),
|
||||
"Incorrect registry lock password for contact");
|
||||
}
|
||||
checkArgument(!Strings.isNullOrEmpty(postInput.password), "Missing key for password");
|
||||
RegistrarContact registrarContact =
|
||||
registrar.getContacts().stream()
|
||||
.filter(contact -> contact.getEmailAddress().equals(userEmail))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Unknown user email %s", userEmail)));
|
||||
checkArgument(
|
||||
registrarContact.verifyRegistryLockPassword(postInput.password),
|
||||
"Incorrect registry lock password for contact");
|
||||
}
|
||||
|
||||
/** Value class that represents the expected input body from the UI request. */
|
||||
@@ -211,7 +211,6 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
private String clientId;
|
||||
private String fullyQualifiedDomainName;
|
||||
private Boolean isLock;
|
||||
private String pocId;
|
||||
private String password;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,23 +25,22 @@
|
||||
<class>google.registry.schema.domain.RegistryLock</class>
|
||||
<class>google.registry.schema.tmch.ClaimsList</class>
|
||||
<class>google.registry.schema.cursor.Cursor</class>
|
||||
<class>google.registry.model.transfer.BaseTransferObject</class>
|
||||
<class>google.registry.schema.tld.PremiumList</class>
|
||||
<class>google.registry.schema.tld.PremiumEntry</class>
|
||||
<class>google.registry.schema.tld.ReservedList</class>
|
||||
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
|
||||
<class>google.registry.model.domain.DesignatedContact</class>
|
||||
<class>google.registry.model.domain.DomainBase</class>
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
<class>org.joda.time.Period</class>
|
||||
<class>google.registry.model.transfer.TransferData</class>
|
||||
<class>google.registry.model.eppcommon.Trid</class>
|
||||
|
||||
<!-- Customized type converters -->
|
||||
<class>google.registry.persistence.BloomFilterConverter</class>
|
||||
<class>google.registry.persistence.CidrAddressBlockListConverter</class>
|
||||
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
|
||||
<class>google.registry.persistence.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.RegistrarPocSetConverter</class>
|
||||
<class>google.registry.persistence.StatusValueSetConverter</class>
|
||||
<class>google.registry.persistence.StringListConverter</class>
|
||||
<class>google.registry.persistence.StringSetConverter</class>
|
||||
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
|
||||
<class>google.registry.persistence.ZonedDateTimeConverter</class>
|
||||
|
||||
|
||||
+1
-3
@@ -25,13 +25,12 @@ 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}. */
|
||||
/** Unit tests for {@link CidrAddressBlockListConverter}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class CidrAddressBlockListUserTypeTest {
|
||||
@Rule
|
||||
@@ -59,7 +58,6 @@ public class CidrAddressBlockListUserTypeTest {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
@Type(type = "google.registry.persistence.CidrAddressBlockListUserType")
|
||||
List<CidrAddressBlock> addresses;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.truth.Expect;
|
||||
import java.util.Collections;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests to verify persistence.xml is valid. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class PersistenceXmlTest {
|
||||
|
||||
@ClassRule public static final Expect expect = Expect.create();
|
||||
|
||||
@Test
|
||||
public void verifyClassTags_containOnlyRequiredClasses() {
|
||||
ImmutableList<Class> managedClassed = PersistenceXmlUtility.getManagedClasses();
|
||||
|
||||
ImmutableList<Class> unnecessaryClasses =
|
||||
managedClassed.stream()
|
||||
.filter(
|
||||
clazz ->
|
||||
!clazz.isAnnotationPresent(Entity.class)
|
||||
&& !AttributeConverter.class.isAssignableFrom(clazz))
|
||||
.collect(toImmutableList());
|
||||
|
||||
ImmutableSet<Class> duplicateClasses =
|
||||
managedClassed.stream()
|
||||
.filter(clazz -> Collections.frequency(managedClassed, clazz) > 1)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
expect
|
||||
.withMessage("Found duplicate <class> tags defined in persistence.xml.")
|
||||
.that(duplicateClasses)
|
||||
.isEmpty();
|
||||
|
||||
expect
|
||||
.withMessage(
|
||||
"Found unnecessary <class> tags defined in persistence.xml. Only entity class and"
|
||||
+ " implementation of AttributeConverter are required to be added in"
|
||||
+ " persistence.xml.")
|
||||
.that(unnecessaryClasses)
|
||||
.isEmpty();
|
||||
}
|
||||
}
|
||||
+6
-38
@@ -18,29 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||
import java.util.Set;
|
||||
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 EnumSetUserType}. */
|
||||
/** Unit tests for {@link StatusValueSetConverter}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class EnumSetUserTypeTest {
|
||||
public class StatusValueSetConverterTest {
|
||||
@Rule
|
||||
public final JpaUnitTestRule jpaRule =
|
||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||
|
||||
public EnumSetUserTypeTest() {}
|
||||
|
||||
@Test
|
||||
public void testRoundTrip() {
|
||||
Set<TestEnum> enums = ImmutableSet.of(TestEnum.BAR, TestEnum.FOO);
|
||||
Set<StatusValue> enums = ImmutableSet.of(StatusValue.INACTIVE, StatusValue.PENDING_DELETE);
|
||||
TestEntity obj = new TestEntity("foo", enums);
|
||||
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
|
||||
@@ -49,45 +47,15 @@ public class EnumSetUserTypeTest {
|
||||
assertThat(persisted.data).isEqualTo(enums);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQuery_succeeds() {
|
||||
Set<TestEnum> enums = ImmutableSet.of(TestEnum.BAR, TestEnum.FOO);
|
||||
TestEntity obj = new TestEntity("foo", enums);
|
||||
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
|
||||
|
||||
assertThat(
|
||||
ImmutableSet.of(
|
||||
getSingleResultFromNativeQuery(
|
||||
"SELECT data[1] FROM \"TestEntity\" WHERE name = 'foo'"),
|
||||
getSingleResultFromNativeQuery(
|
||||
"SELECT data[2] FROM \"TestEntity\" WHERE name = 'foo'")))
|
||||
.containsExactly("BAR", "FOO");
|
||||
}
|
||||
|
||||
private static Object getSingleResultFromNativeQuery(String sql) {
|
||||
return jpaTm()
|
||||
.transact(() -> jpaTm().getEntityManager().createNativeQuery(sql).getSingleResult());
|
||||
}
|
||||
|
||||
enum TestEnum {
|
||||
FOO,
|
||||
BAR,
|
||||
BAZ;
|
||||
|
||||
public static class TestEnumType extends EnumSetUserType<TestEnum> {}
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
static class TestEntity {
|
||||
@Id String name;
|
||||
|
||||
@Type(type = "google.registry.persistence.EnumSetUserTypeTest$TestEnum$TestEnumType")
|
||||
Set<TestEnum> data;
|
||||
Set<StatusValue> data;
|
||||
|
||||
TestEntity() {}
|
||||
|
||||
TestEntity(String name, Set<TestEnum> data) {
|
||||
TestEntity(String name, Set<StatusValue> data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
+2
-4
@@ -26,15 +26,14 @@ import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NoResultException;
|
||||
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 StringListUserType}. */
|
||||
/** Unit tests for {@link StringListConverter}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class StringListUserTypeTest {
|
||||
public class StringListConverterTest {
|
||||
@Rule
|
||||
public final JpaUnitTestRule jpaRule =
|
||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||
@@ -123,7 +122,6 @@ public class StringListUserTypeTest {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
@Type(type = "google.registry.persistence.StringListUserType")
|
||||
List<String> tlds;
|
||||
|
||||
private TestEntity() {}
|
||||
+2
-4
@@ -24,15 +24,14 @@ import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||
import java.util.Set;
|
||||
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 StringSetUserType}. */
|
||||
/** Unit tests for {@link StringSetConverter}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class StringSetUserTypeTest {
|
||||
public class StringSetConverterTest {
|
||||
@Rule
|
||||
public final JpaUnitTestRule jpaRule =
|
||||
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
|
||||
@@ -70,7 +69,6 @@ public class StringSetUserTypeTest {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
@Type(type = "google.registry.persistence.StringSetUserType")
|
||||
Set<String> tlds;
|
||||
|
||||
private TestEntity() {}
|
||||
+20
-32
@@ -132,6 +132,7 @@ public final class RegistryLockPostActionTest {
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
// we should still email the admin user's email address
|
||||
assertSuccess(response, "unlock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ public final class RegistryLockPostActionTest {
|
||||
|
||||
@Test
|
||||
public void testSuccess_adminUser() throws Exception {
|
||||
// Admin user should be able to lock/unlock regardless
|
||||
// Admin user should be able to lock/unlock regardless -- and we use the admin user's email
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
@@ -179,6 +180,20 @@ public final class RegistryLockPostActionTest {
|
||||
assertSuccess(response, "lock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_adminUser_doesNotRequirePassword() throws Exception {
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true));
|
||||
assertSuccess(response, "lock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noInput() {
|
||||
Map<String, ?> response = action.handleJsonRequest(null);
|
||||
@@ -231,20 +246,21 @@ public final class RegistryLockPostActionTest {
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com"));
|
||||
"isLock", true));
|
||||
assertFailureWithMessage(response, "Missing key for password");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_notEnabledForRegistrarContact() {
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, false)));
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "johndoe@theregistrar.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
|
||||
}
|
||||
@@ -257,7 +273,6 @@ public final class RegistryLockPostActionTest {
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "badPassword"));
|
||||
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
|
||||
}
|
||||
@@ -270,36 +285,10 @@ public final class RegistryLockPostActionTest {
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Unknown domain bad.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noPocId() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Missing key for pocId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_invalidPocId() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"pocId", "someotherpoc@crr.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Unknown registrar POC ID someotherpoc@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_previousLockUnlocked() throws Exception {
|
||||
RegistryLockDao.save(
|
||||
@@ -357,7 +346,6 @@ public final class RegistryLockPostActionTest {
|
||||
"isLock", lock,
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "hi");
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ spotless {
|
||||
format 'misc', {
|
||||
clearSteps()
|
||||
target '**/*.gradle'
|
||||
targetExclude '**/cloudbuild-caches/**'
|
||||
trimTrailingWhitespace()
|
||||
indentWithSpaces(2)
|
||||
endWithNewline()
|
||||
|
||||
@@ -41,12 +41,6 @@ dependencies {
|
||||
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||
}
|
||||
|
||||
test {
|
||||
// Temporarily allow non-CA cert as trust anchor (legacy behavior) in tests.
|
||||
// TODO(weiminyu): generate test cert as a CA cert.
|
||||
systemProperty 'jdk.security.allowNonCaAnchor', 'true'
|
||||
}
|
||||
|
||||
// Make testing artifacts available to be depended up on by other projects.
|
||||
task testJar(type: Jar) {
|
||||
classifier = 'test'
|
||||
|
||||
+5
-5
@@ -25,7 +25,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -163,9 +163,9 @@ public final class CertificateSupplierModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
static SelfSignedCertificate provideSelfSignedCertificate() {
|
||||
static SelfSignedCaCertificate provideSelfSignedCertificate() {
|
||||
try {
|
||||
return new SelfSignedCertificate();
|
||||
return SelfSignedCaCertificate.create();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public final class CertificateSupplierModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
@SelfSigned
|
||||
static Supplier<PrivateKey> provideSelfSignedPrivateKeySupplier(SelfSignedCertificate ssc) {
|
||||
static Supplier<PrivateKey> provideSelfSignedPrivateKeySupplier(SelfSignedCaCertificate ssc) {
|
||||
return Suppliers.ofInstance(ssc.key());
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public final class CertificateSupplierModule {
|
||||
@Provides
|
||||
@SelfSigned
|
||||
static Supplier<ImmutableList<X509Certificate>> provideSelfSignedCertificatesSupplier(
|
||||
SelfSignedCertificate ssc) {
|
||||
SelfSignedCaCertificate ssc) {
|
||||
return Suppliers.ofInstance(ImmutableList.of(ssc.cert()));
|
||||
}
|
||||
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
// 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.networking.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
|
||||
/** A self-signed certificate authority (CA) cert for use in tests. */
|
||||
// TODO(weiminyu): make this class test-only. Requires refactor in proxy and prober.
|
||||
public class SelfSignedCaCertificate {
|
||||
|
||||
private static final String DEFAULT_ISSUER_FQDN = "registry-test";
|
||||
private static final Date DEFAULT_NOT_BEFORE =
|
||||
Date.from(Instant.now().minus(Duration.ofHours(1)));
|
||||
private static final Date DEFAULT_NOT_AFTER = Date.from(Instant.now().plus(Duration.ofDays(1)));
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
|
||||
private static final KeyPairGenerator keyGen = createKeyPairGenerator();
|
||||
|
||||
private final PrivateKey privateKey;
|
||||
private final X509Certificate cert;
|
||||
|
||||
public SelfSignedCaCertificate(PrivateKey privateKey, X509Certificate cert) {
|
||||
this.privateKey = privateKey;
|
||||
this.cert = cert;
|
||||
}
|
||||
|
||||
public PrivateKey key() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public X509Certificate cert() {
|
||||
return cert;
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create() throws Exception {
|
||||
return create(
|
||||
keyGen.generateKeyPair(), DEFAULT_ISSUER_FQDN, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create(String fqdn) throws Exception {
|
||||
return create(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create(String fqdn, Date from, Date to) throws Exception {
|
||||
return create(keyGen.generateKeyPair(), fqdn, from, to);
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create(KeyPair keyPair, String fqdn, Date from, Date to)
|
||||
throws Exception {
|
||||
return new SelfSignedCaCertificate(keyPair.getPrivate(), createCaCert(keyPair, fqdn, from, to));
|
||||
}
|
||||
|
||||
static KeyPairGenerator createKeyPairGenerator() {
|
||||
try {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", PROVIDER);
|
||||
keyGen.initialize(2048, new SecureRandom());
|
||||
return keyGen;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a self-signed Certificate Authority (CA) certificate. */
|
||||
static X509Certificate createCaCert(KeyPair keyPair, String fqdn, Date from, Date to)
|
||||
throws Exception {
|
||||
X500Name owner = new X500Name("CN=" + fqdn);
|
||||
ContentSigner signer =
|
||||
new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
|
||||
X509v3CertificateBuilder builder =
|
||||
new JcaX509v3CertificateBuilder(
|
||||
owner, new BigInteger(64, RANDOM), from, to, owner, keyPair.getPublic());
|
||||
|
||||
// Mark cert as CA by adding basicConstraint with cA=true to the builder
|
||||
BasicConstraints basicConstraints = new BasicConstraints(true);
|
||||
builder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints);
|
||||
|
||||
X509CertificateHolder certHolder = builder.build(signer);
|
||||
return new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -21,6 +21,7 @@ import static google.registry.networking.handler.SslInitializerTestUtils.signKey
|
||||
import static google.registry.networking.handler.SslInitializerTestUtils.verifySslExcpetion;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
@@ -35,7 +36,6 @@ import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertPathBuilderException;
|
||||
@@ -153,7 +153,7 @@ public class SslClientInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testFailure_defaultTrustManager_rejectSelfSignedCert() throws Exception {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress =
|
||||
new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider);
|
||||
nettyRule.setUpServer(localAddress, getServerHandler(false, ssc.key(), ssc.cert()));
|
||||
@@ -177,7 +177,7 @@ public class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
X509Certificate cert = signKeyPair(ssc, keyPair, SSL_HOST);
|
||||
|
||||
// Set up the server to use the signed cert and private key to perform handshake;
|
||||
@@ -206,7 +206,7 @@ public class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
X509Certificate cert =
|
||||
signKeyPair(
|
||||
ssc,
|
||||
@@ -240,7 +240,7 @@ public class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
X509Certificate cert =
|
||||
signKeyPair(
|
||||
ssc,
|
||||
@@ -272,8 +272,8 @@ public class SslClientInitializerTest {
|
||||
new LocalAddress(
|
||||
"CUSTOM_TRUST_MANAGER_ACCEPT_SELF_SIGNED_CERT_CLIENT_CERT_REQUIRED_" + sslProvider);
|
||||
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCertificate clientSsc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
|
||||
// Set up the server to require client certificate.
|
||||
nettyRule.setUpServer(localAddress, getServerHandler(true, serverSsc.key(), serverSsc.cert()));
|
||||
@@ -311,7 +311,7 @@ public class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
X509Certificate cert = signKeyPair(ssc, keyPair, "wrong.com");
|
||||
|
||||
// Set up the server to use the signed cert and private key to perform handshake;
|
||||
|
||||
+24
-28
@@ -18,15 +18,14 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -34,17 +33,13 @@ import java.util.Date;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.crypto.util.PrivateKeyFactory;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
|
||||
/**
|
||||
* Utility class that provides methods used by {@link SslClientInitializerTest} and {@link
|
||||
@@ -52,16 +47,23 @@ import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
|
||||
*/
|
||||
public final class SslInitializerTestUtils {
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
|
||||
private static final KeyPairGenerator KEY_PAIR_GENERATOR = getKeyPairGenerator();
|
||||
|
||||
private SslInitializerTestUtils() {}
|
||||
|
||||
private static KeyPairGenerator getKeyPairGenerator() {
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER);
|
||||
keyPairGenerator.initialize(2048, new SecureRandom());
|
||||
return keyPairGenerator;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyPair getKeyPair() throws Exception {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
|
||||
keyPairGenerator.initialize(2048, new SecureRandom());
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
return KEY_PAIR_GENERATOR.generateKeyPair();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,26 +73,20 @@ public final class SslInitializerTestUtils {
|
||||
* @return signed public key (of the key pair) certificate
|
||||
*/
|
||||
public static X509Certificate signKeyPair(
|
||||
SelfSignedCertificate ssc, KeyPair keyPair, String hostname, Date from, Date to)
|
||||
SelfSignedCaCertificate ssc, KeyPair keyPair, String hostname, Date from, Date to)
|
||||
throws Exception {
|
||||
X500Name subjectDnName = new X500Name("CN=" + hostname);
|
||||
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis());
|
||||
X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName());
|
||||
SubjectPublicKeyInfo subPubKeyInfo =
|
||||
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
|
||||
AlgorithmIdentifier sigAlgId =
|
||||
new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption");
|
||||
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
|
||||
|
||||
ContentSigner sigGen =
|
||||
new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
|
||||
.build(PrivateKeyFactory.createKey(ssc.key().getEncoded()));
|
||||
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(ssc.key());
|
||||
X509v3CertificateBuilder v3CertGen =
|
||||
new X509v3CertificateBuilder(
|
||||
issuerDnName, serialNumber, from, to, subjectDnName, subPubKeyInfo);
|
||||
new JcaX509v3CertificateBuilder(
|
||||
issuerDnName, serialNumber, from, to, subjectDnName, keyPair.getPublic());
|
||||
|
||||
X509CertificateHolder certificateHolder = v3CertGen.build(sigGen);
|
||||
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder);
|
||||
return new JcaX509CertificateConverter()
|
||||
.setProvider(PROVIDER)
|
||||
.getCertificate(certificateHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +96,7 @@ public final class SslInitializerTestUtils {
|
||||
* @return signed public key (of the key pair) certificate
|
||||
*/
|
||||
public static X509Certificate signKeyPair(
|
||||
SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception {
|
||||
SelfSignedCaCertificate ssc, KeyPair keyPair, String hostname) throws Exception {
|
||||
return signKeyPair(
|
||||
ssc,
|
||||
keyPair,
|
||||
|
||||
+16
-16
@@ -23,6 +23,7 @@ import static google.registry.networking.handler.SslServerInitializer.CLIENT_CER
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
@@ -33,7 +34,6 @@ import io.netty.handler.ssl.OpenSsl;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateException;
|
||||
@@ -127,7 +127,7 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testSuccess_swappedInitializerWithSslHandler() throws Exception {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SslServerInitializer<EmbeddedChannel> sslServerInitializer =
|
||||
new SslServerInitializer<>(
|
||||
true,
|
||||
@@ -147,12 +147,12 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testSuccess_trustAnyClientCert() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("TRUST_ANY_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
localAddress, getServerHandler(true, false, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCertificate clientSsc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
nettyRule.setUpClient(
|
||||
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
|
||||
|
||||
@@ -168,13 +168,13 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testFailure_clientCertExpired() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
localAddress, getServerHandler(true, true, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCertificate clientSsc =
|
||||
new SelfSignedCertificate(
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().minus(Duration.ofDays(2))),
|
||||
Date.from(Instant.now().minus(Duration.ofDays(1))));
|
||||
@@ -189,13 +189,13 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testFailure_clientCertNotYetValid() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
localAddress, getServerHandler(true, true, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCertificate clientSsc =
|
||||
new SelfSignedCertificate(
|
||||
SelfSignedCaCertificate clientSsc =
|
||||
SelfSignedCaCertificate.create(
|
||||
"CLIENT",
|
||||
Date.from(Instant.now().plus(Duration.ofDays(1))),
|
||||
Date.from(Instant.now().plus(Duration.ofDays(2))));
|
||||
@@ -210,7 +210,7 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testSuccess_doesNotRequireClientCert() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("DOES_NOT_REQUIRE_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
@@ -230,7 +230,7 @@ public class SslServerInitializerTest {
|
||||
@Test
|
||||
public void testSuccess_CertSignedByOtherCA() throws Exception {
|
||||
// The self-signed cert of the CA.
|
||||
SelfSignedCertificate caSsc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate caSsc = SelfSignedCaCertificate.create();
|
||||
KeyPair keyPair = getKeyPair();
|
||||
X509Certificate serverCert = signKeyPair(caSsc, keyPair, SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CERT_SIGNED_BY_OTHER_CA_" + sslProvider);
|
||||
@@ -244,7 +244,7 @@ public class SslServerInitializerTest {
|
||||
// Serving both the server cert, and the CA cert
|
||||
serverCert,
|
||||
caSsc.cert()));
|
||||
SelfSignedCertificate clientSsc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
nettyRule.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
@@ -263,7 +263,7 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testFailure_requireClientCertificate() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("REQUIRE_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
@@ -285,12 +285,12 @@ public class SslServerInitializerTest {
|
||||
|
||||
@Test
|
||||
public void testFailure_wrongHostnameInCertificate() throws Exception {
|
||||
SelfSignedCertificate serverSsc = new SelfSignedCertificate("wrong.com");
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create("wrong.com");
|
||||
LocalAddress localAddress = new LocalAddress("WRONG_HOSTNAME_" + sslProvider);
|
||||
|
||||
nettyRule.setUpServer(
|
||||
localAddress, getServerHandler(true, false, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCertificate clientSsc = new SelfSignedCertificate();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
nettyRule.setUpClient(
|
||||
localAddress, getClientHandler(serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
|
||||
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@ import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.networking.module.CertificateSupplierModule.Mode;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.security.KeyPair;
|
||||
@@ -47,7 +47,7 @@ import org.junit.runners.JUnit4;
|
||||
@RunWith(JUnit4.class)
|
||||
public class CertificateSupplierModuleTest {
|
||||
|
||||
private SelfSignedCertificate ssc;
|
||||
private SelfSignedCaCertificate ssc;
|
||||
private PrivateKey key;
|
||||
private Certificate cert;
|
||||
private TestComponent component;
|
||||
@@ -62,7 +62,7 @@ public class CertificateSupplierModuleTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ssc = new SelfSignedCertificate();
|
||||
ssc = SelfSignedCaCertificate.create();
|
||||
KeyPair keyPair = getKeyPair();
|
||||
key = keyPair.getPrivate();
|
||||
cert = signKeyPair(ssc, keyPair, "example.tld");
|
||||
|
||||
@@ -23,6 +23,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.testing.FakeClock;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -34,7 +35,6 @@ import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import java.security.cert.X509Certificate;
|
||||
import org.junit.Before;
|
||||
@@ -123,7 +123,7 @@ public class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testComponent = makeTestComponent(new FakeClock());
|
||||
certificate = new SelfSignedCertificate().cert();
|
||||
certificate = SelfSignedCaCertificate.create().cert();
|
||||
initializeChannel(
|
||||
ch -> {
|
||||
ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS);
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import google.registry.networking.util.SelfSignedCaCertificate;
|
||||
import google.registry.proxy.TestUtils;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.proxy.metric.FrontendMetrics;
|
||||
@@ -41,7 +42,6 @@ import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import java.security.cert.X509Certificate;
|
||||
import org.junit.Before;
|
||||
@@ -114,7 +114,7 @@ public class EppServiceHandlerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
clientCertificate = new SelfSignedCertificate().cert();
|
||||
clientCertificate = SelfSignedCaCertificate.create().cert();
|
||||
channel = setUpNewChannel(eppServiceHandler);
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ public class EppServiceHandlerTest {
|
||||
HELLO.getBytes(UTF_8),
|
||||
metrics);
|
||||
EmbeddedChannel channel2 = setUpNewChannel(eppServiceHandler2);
|
||||
X509Certificate clientCertificate2 = new SelfSignedCertificate().cert();
|
||||
X509Certificate clientCertificate2 = SelfSignedCaCertificate.create().cert();
|
||||
setHandshakeSuccess(channel2, clientCertificate2);
|
||||
String certHash2 = getCertificateHash(clientCertificate2);
|
||||
|
||||
|
||||
@@ -27,15 +27,23 @@ fi
|
||||
|
||||
environment="$1"
|
||||
dest="$2"
|
||||
gcs_prefix="storage.googleapis.com/domain-registry-maven-repository"
|
||||
gcs_prefix="gcs://domain-registry-maven-repository"
|
||||
|
||||
# Let Gradle put its caches (dependency cache and build cache) in the source
|
||||
# tree. This allows sharing of the caches between steps in a Cloud Build
|
||||
# task. (See ./cloudbuild-nomulus.yaml, which calls this script in several
|
||||
# steps). If left at their default location, the caches will be lost after
|
||||
# each step.
|
||||
# Note: must be consistent with value in ./cloudbuild-nomulus.yaml
|
||||
export GRADLE_USER_HOME="./cloudbuild-caches"
|
||||
|
||||
if [ "${environment}" == tool ]
|
||||
then
|
||||
mkdir -p "${dest}"
|
||||
|
||||
./gradlew clean :core:buildToolImage \
|
||||
-PmavenUrl=https://"${gcs_prefix}"/maven \
|
||||
-PpluginsUrl=https://"${gcs_prefix}"/plugins
|
||||
-PmavenUrl="${gcs_prefix}"/maven \
|
||||
-PpluginsUrl="${gcs_prefix}"/plugins
|
||||
|
||||
mv core/build/libs/nomulus.jar "${dest}"
|
||||
else
|
||||
@@ -43,8 +51,8 @@ else
|
||||
mkdir -p "${dest}"
|
||||
|
||||
./gradlew clean stage -Penvironment="${environment}" \
|
||||
-PmavenUrl=https://"${gcs_prefix}"/maven \
|
||||
-PpluginsUrl=https://"${gcs_prefix}"/plugins
|
||||
-PmavenUrl="${gcs_prefix}"/maven \
|
||||
-PpluginsUrl="${gcs_prefix}"/plugins
|
||||
|
||||
for service in default pubapi backend tools
|
||||
do
|
||||
|
||||
@@ -21,7 +21,15 @@ steps:
|
||||
args: ['mkdir', 'nomulus']
|
||||
# Run tests
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
args: ['./gradlew', 'test', '-PskipDockerIncompatibleTests=true']
|
||||
# Set home for Gradle caches. Must be consistent with last step below
|
||||
# and ./build_nomulus_for_env.sh
|
||||
env: [ 'GRADLE_USER_HOME=./cloudbuild-caches' ]
|
||||
args: ['./gradlew',
|
||||
'test',
|
||||
'-PskipDockerIncompatibleTests=true',
|
||||
'-PmavenUrl=gcs://domain-registry-maven-repository/maven',
|
||||
'-PpluginsUrl=gcs://domain-registry-maven-repository/plugins'
|
||||
]
|
||||
# Build the tool binary and image.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
args: ['release/build_nomulus_for_env.sh', 'tool', 'output']
|
||||
@@ -70,20 +78,23 @@ steps:
|
||||
# server/schema compatibility tests.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
# Set home for Gradle caches. Must be consistent with second step above
|
||||
# and ./build_nomulus_for_env.sh
|
||||
env: [ 'GRADLE_USER_HOME=./cloudbuild-caches' ]
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
./gradlew \
|
||||
:db:publish \
|
||||
-PmavenUrl=https://storage.googleapis.com/domain-registry-maven-repository/maven \
|
||||
-PpluginsUrl=https://storage.googleapis.com/domain-registry-maven-repository/plugins \
|
||||
-PmavenUrl=gcs://domain-registry-maven-repository/maven \
|
||||
-PpluginsUrl=gcs://domain-registry-maven-repository/plugins \
|
||||
-Ppublish_repo=gcs://${PROJECT_ID}-deployed-tags/maven \
|
||||
-Pschema_version=${TAG_NAME}
|
||||
./gradlew \
|
||||
:core:publish \
|
||||
-PmavenUrl=https://storage.googleapis.com/domain-registry-maven-repository/maven \
|
||||
-PpluginsUrl=https://storage.googleapis.com/domain-registry-maven-repository/plugins \
|
||||
-PmavenUrl=gcs://domain-registry-maven-repository/maven \
|
||||
-PpluginsUrl=gcs://domain-registry-maven-repository/plugins \
|
||||
-Ppublish_repo=gcs://${PROJECT_ID}-deployed-tags/maven \
|
||||
-Pnomulus_version=${TAG_NAME}
|
||||
# Upload schema jar for use by schema deployment.
|
||||
|
||||
@@ -19,8 +19,8 @@ steps:
|
||||
- ./gradlew
|
||||
- :proxy:test
|
||||
- :proxy:buildProxyImage
|
||||
- -PmavenUrl=https://storage.googleapis.com/domain-registry-maven-repository/maven
|
||||
- -PpluginsUrl=https://storage.googleapis.com/domain-registry-maven-repository/plugins
|
||||
- -PmavenUrl=gcs://domain-registry-maven-repository/maven
|
||||
- -PpluginsUrl=gcs://domain-registry-maven-repository/plugins
|
||||
# Tag and push the image. We can't let Cloud Build's default processing do that for us
|
||||
# because we need to push the image before we can sign it in the following step.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
|
||||
Reference in New Issue
Block a user