mirror of
https://github.com/google/nomulus
synced 2026-05-25 01:01:57 +00:00
Compare commits
11 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85b588b51f | ||
|
|
572b7101cb | ||
|
|
445825957d | ||
|
|
7ab76f3573 | ||
|
|
9e3c58989a | ||
|
|
cf9c1ec7c3 | ||
|
|
69ea87be31 | ||
|
|
779d0c9d37 | ||
|
|
2855944214 | ||
|
|
992d1c1349 | ||
|
|
f50290ce1d |
@@ -347,6 +347,7 @@ subprojects {
|
||||
|
||||
def services = [':services:default',
|
||||
':services:backend',
|
||||
':services:bsa',
|
||||
':services:tools',
|
||||
':services:pubapi']
|
||||
|
||||
|
||||
21
core/src/main/java/google/registry/bsa/BlockList.java
Normal file
21
core/src/main/java/google/registry/bsa/BlockList.java
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2023 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.bsa;
|
||||
|
||||
/** Identifiers of the BSA lists with blocking labels. */
|
||||
public enum BlockList {
|
||||
BLOCK,
|
||||
BLOCK_PLUS;
|
||||
}
|
||||
20
core/src/main/java/google/registry/bsa/DownloadStage.java
Normal file
20
core/src/main/java/google/registry/bsa/DownloadStage.java
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2023 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.bsa;
|
||||
|
||||
/** The processing stages of a download. */
|
||||
public enum DownloadStage {
|
||||
DOWNLOAD;
|
||||
}
|
||||
108
core/src/main/java/google/registry/bsa/IdnChecker.java
Normal file
108
core/src/main/java/google/registry/bsa/IdnChecker.java
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2023 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.bsa;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Maps.transformValues;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.tldconfig.idn.IdnLabelValidator;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.Clock;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Checks labels' validity wrt Idns in TLDs enrolled with BSA.
|
||||
*
|
||||
* <p>Each instance takes a snapshot of the TLDs at instantiation time, and should be limited to the
|
||||
* Request scope.
|
||||
*/
|
||||
public class IdnChecker {
|
||||
private static final IdnLabelValidator IDN_LABEL_VALIDATOR = new IdnLabelValidator();
|
||||
|
||||
private final ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> idnToTlds;
|
||||
private final ImmutableSet<Tld> allTlds;
|
||||
|
||||
@Inject
|
||||
IdnChecker(Clock clock) {
|
||||
this.idnToTlds = getIdnToTldMap(clock.nowUtc());
|
||||
allTlds = idnToTlds.values().stream().flatMap(ImmutableSet::stream).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
// TODO(11/30/2023): Remove below when new Tld schema is deployed and the `getBsaEnrollStartTime`
|
||||
// method is no longer hardcoded.
|
||||
@VisibleForTesting
|
||||
IdnChecker(ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> idnToTlds) {
|
||||
this.idnToTlds = idnToTlds;
|
||||
allTlds = idnToTlds.values().stream().flatMap(ImmutableSet::stream).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/** Returns all IDNs in which the {@code label} is valid. */
|
||||
ImmutableSet<IdnTableEnum> getAllValidIdns(String label) {
|
||||
return idnToTlds.keySet().stream()
|
||||
.filter(idnTable -> idnTable.getTable().isValidLabel(label))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TLDs that support at least one IDN in the {@code idnTables}.
|
||||
*
|
||||
* @param idnTables String names of {@link IdnTableEnum} values
|
||||
*/
|
||||
public ImmutableSet<Tld> getSupportingTlds(ImmutableSet<String> idnTables) {
|
||||
return idnTables.stream()
|
||||
.map(IdnTableEnum::valueOf)
|
||||
.filter(idnToTlds::containsKey)
|
||||
.map(idnToTlds::get)
|
||||
.flatMap(ImmutableSet::stream)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TLDs that do not support any IDN in the {@code idnTables}.
|
||||
*
|
||||
* @param idnTables String names of {@link IdnTableEnum} values
|
||||
*/
|
||||
public SetView<Tld> getForbiddingTlds(ImmutableSet<String> idnTables) {
|
||||
return Sets.difference(allTlds, getSupportingTlds(idnTables));
|
||||
}
|
||||
|
||||
private static boolean isEnrolledWithBsa(Tld tld, DateTime now) {
|
||||
DateTime enrollTime = tld.getBsaEnrollStartTime();
|
||||
return enrollTime != null && enrollTime.isBefore(now);
|
||||
}
|
||||
|
||||
private static ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> getIdnToTldMap(DateTime now) {
|
||||
ImmutableMultimap.Builder<IdnTableEnum, Tld> idnToTldMap = new ImmutableMultimap.Builder();
|
||||
Tlds.getTldEntitiesOfType(TldType.REAL).stream()
|
||||
.filter(tld -> isEnrolledWithBsa(tld, now))
|
||||
.forEach(
|
||||
tld -> {
|
||||
for (IdnTableEnum idn : IDN_LABEL_VALIDATOR.getIdnTablesForTld(tld)) {
|
||||
idnToTldMap.put(idn, tld);
|
||||
}
|
||||
});
|
||||
return ImmutableMap.copyOf(transformValues(idnToTldMap.build().asMap(), ImmutableSet::copyOf));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2023 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.bsa;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Action(
|
||||
service = Service.BSA,
|
||||
path = PlaceholderAction.PATH,
|
||||
method = Action.Method.GET,
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class PlaceholderAction implements Runnable {
|
||||
private final Response response;
|
||||
|
||||
static final String PATH = "/_dr/task/bsaDownload";
|
||||
|
||||
@Inject
|
||||
public PlaceholderAction(Response response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload("Hello World");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import google.registry.bsa.persistence.BsaDomainInUse.BsaDomainInUseId;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
|
||||
/** A domain matching a BSA label but is in use (registered or reserved), so cannot be blocked. */
|
||||
@Entity
|
||||
@IdClass(BsaDomainInUseId.class)
|
||||
public class BsaDomainInUse {
|
||||
@Id String label;
|
||||
@Id String tld;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
Reason reason;
|
||||
|
||||
/**
|
||||
* Creation time of this record, which is the most recent time when the domain was detected to be
|
||||
* in use wrt BSA. It may be during the processing of a download, or during some other job that
|
||||
* refreshes the state.
|
||||
*
|
||||
* <p>This field is for information only.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Column(nullable = false)
|
||||
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
// For Hibernate
|
||||
BsaDomainInUse() {}
|
||||
|
||||
public BsaDomainInUse(String label, String tld, Reason reason) {
|
||||
this.label = label;
|
||||
this.tld = tld;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BsaDomainInUse)) {
|
||||
return false;
|
||||
}
|
||||
BsaDomainInUse that = (BsaDomainInUse) o;
|
||||
return Objects.equal(label, that.label)
|
||||
&& Objects.equal(tld, that.tld)
|
||||
&& reason == that.reason
|
||||
&& Objects.equal(createTime, that.createTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(label, tld, reason, createTime);
|
||||
}
|
||||
|
||||
enum Reason {
|
||||
REGISTERED,
|
||||
RESERVED;
|
||||
}
|
||||
|
||||
static class BsaDomainInUseId implements Serializable {
|
||||
|
||||
private String label;
|
||||
private String tld;
|
||||
|
||||
// For Hibernate
|
||||
BsaDomainInUseId() {}
|
||||
|
||||
BsaDomainInUseId(String label, String tld) {
|
||||
this.label = label;
|
||||
this.tld = tld;
|
||||
}
|
||||
}
|
||||
|
||||
static VKey<BsaDomainInUse> vKey(String label, String tld) {
|
||||
return VKey.create(BsaDomainInUse.class, new BsaDomainInUseId(label, tld));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.bsa.DownloadStage.DOWNLOAD;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.bsa.BlockList;
|
||||
import google.registry.bsa.DownloadStage;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Records of ongoing and completed download jobs. */
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "creationTime")})
|
||||
public class BsaDownload {
|
||||
|
||||
private static final Joiner CSV_JOINER = Joiner.on(',');
|
||||
private static final Splitter CSV_SPLITTER = Splitter.on(',');
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long jobId;
|
||||
|
||||
@Column(nullable = false)
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
@Column(nullable = false)
|
||||
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
|
||||
|
||||
@Column(nullable = false)
|
||||
String blockListChecksums = "";
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
DownloadStage stage = DOWNLOAD;
|
||||
|
||||
BsaDownload() {}
|
||||
|
||||
public long getJobId() {
|
||||
return jobId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting time of this job as a string, which can be used as folder name on GCS when
|
||||
* storing download data.
|
||||
*/
|
||||
public String getJobName() {
|
||||
return creationTime.getTimestamp().toString();
|
||||
}
|
||||
|
||||
public DownloadStage getStage() {
|
||||
return this.stage;
|
||||
}
|
||||
|
||||
BsaDownload setStage(DownloadStage stage) {
|
||||
this.stage = stage;
|
||||
return this;
|
||||
}
|
||||
|
||||
DateTime getCreationTime() {
|
||||
return creationTime.getTimestamp();
|
||||
}
|
||||
|
||||
BsaDownload setBlockListChecksums(ImmutableMap<BlockList, String> checksums) {
|
||||
blockListChecksums =
|
||||
CSV_JOINER.withKeyValueSeparator("=").join(ImmutableSortedMap.copyOf(checksums));
|
||||
return this;
|
||||
}
|
||||
|
||||
ImmutableMap<BlockList, String> getChecksums() {
|
||||
if (blockListChecksums.isEmpty()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
return CSV_SPLITTER.withKeyValueSeparator('=').split(blockListChecksums).entrySet().stream()
|
||||
.collect(
|
||||
toImmutableMap(entry -> BlockList.valueOf(entry.getKey()), entry -> entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BsaDownload)) {
|
||||
return false;
|
||||
}
|
||||
BsaDownload that = (BsaDownload) o;
|
||||
return Objects.equal(creationTime, that.creationTime)
|
||||
&& Objects.equal(updateTime, that.updateTime)
|
||||
&& Objects.equal(blockListChecksums, that.blockListChecksums)
|
||||
&& stage == that.stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(creationTime, updateTime, blockListChecksums, stage);
|
||||
}
|
||||
|
||||
static VKey<BsaDownload> vKey(long jobId) {
|
||||
return VKey.create(BsaDownload.class, Long.valueOf(jobId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Specifies a second-level TLD name that should be blocked from registration in all TLDs except by
|
||||
* the label's owner.
|
||||
*
|
||||
* <p>The label is valid (wrt IDN) in at least one TLD.
|
||||
*/
|
||||
@Entity
|
||||
public final class BsaLabel {
|
||||
|
||||
@Id String label;
|
||||
|
||||
/**
|
||||
* Creation time of this label. This field is for human use, and should give the name of the GCS
|
||||
* folder that contains the downloaded BSA data.
|
||||
*
|
||||
* <p>See {@link BsaDownload#getCreationTime} and {@link BsaDownload#getJobName} for more
|
||||
* information.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Column(nullable = false)
|
||||
DateTime creationTime;
|
||||
|
||||
// For Hibernate.
|
||||
BsaLabel() {}
|
||||
|
||||
BsaLabel(String label, DateTime creationTime) {
|
||||
this.label = label;
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
/** Returns the label to be blocked. */
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BsaLabel)) {
|
||||
return false;
|
||||
}
|
||||
BsaLabel label1 = (BsaLabel) o;
|
||||
return Objects.equal(label, label1.label) && Objects.equal(creationTime, label1.creationTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(label, creationTime);
|
||||
}
|
||||
|
||||
static VKey<BsaLabel> vKey(String label) {
|
||||
return VKey.create(BsaLabel.class, label);
|
||||
}
|
||||
}
|
||||
@@ -1192,6 +1192,12 @@ public final class RegistryConfig {
|
||||
return config.auth.oauthClientId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("fallbackOauthClientId")
|
||||
public static String provideFallbackOauthClientId(RegistryConfigSettings config) {
|
||||
return config.auth.fallbackOauthClientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the OAuth scopes required for accessing Google APIs using the default credential.
|
||||
*/
|
||||
@@ -1452,6 +1458,15 @@ public final class RegistryConfig {
|
||||
return makeUrl(CONFIG_SETTINGS.get().gcpProject.backendServiceUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the Nomulus app bsa HTTP server.
|
||||
*
|
||||
* <p>This is used by the {@code nomulus} tool to connect to the App Engine remote API.
|
||||
*/
|
||||
public static URL getBsaServer() {
|
||||
return makeUrl(CONFIG_SETTINGS.get().gcpProject.bsaServiceUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the Nomulus app tools HTTP server.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,7 @@ public class RegistryConfigSettings {
|
||||
public boolean isLocal;
|
||||
public String defaultServiceUrl;
|
||||
public String backendServiceUrl;
|
||||
public String bsaServiceUrl;
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
}
|
||||
@@ -61,6 +62,7 @@ public class RegistryConfigSettings {
|
||||
public static class Auth {
|
||||
public List<String> allowedServiceAccountEmails;
|
||||
public String oauthClientId;
|
||||
public String fallbackOauthClientId;
|
||||
}
|
||||
|
||||
/** Configuration options for accessing Google APIs. */
|
||||
|
||||
@@ -20,9 +20,11 @@ gcpProject:
|
||||
# URLs of the services for the project.
|
||||
defaultServiceUrl: https://default.example.com
|
||||
backendServiceUrl: https://backend.example.com
|
||||
bsaServiceUrl: https://bsa.example.com
|
||||
toolsServiceUrl: https://tools.example.com
|
||||
pubapiServiceUrl: https://pubapi.example.com
|
||||
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
domainName: domain-registry.example
|
||||
@@ -321,6 +323,10 @@ auth:
|
||||
# the same as this one.
|
||||
oauthClientId: iap-oauth-clientid
|
||||
|
||||
# Same as above, but serve as a fallback, so we can switch the client ID of
|
||||
# the proxy without downtime.
|
||||
fallbackOauthClientId: fallback-oauth-clientid
|
||||
|
||||
credentialOAuth:
|
||||
# OAuth scopes required for accessing Google APIs using the default
|
||||
# credential.
|
||||
|
||||
31
core/src/main/java/google/registry/env/alpha/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
31
core/src/main/java/google/registry/env/alpha/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>100</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="alpha"/>
|
||||
</system-properties>
|
||||
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-alpha/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1m"/>
|
||||
</static-files>
|
||||
</appengine-web-app>
|
||||
@@ -31,6 +31,12 @@ encoding="UTF-8"?>
|
||||
<context-root>backend</context-root>
|
||||
</web>
|
||||
</module>
|
||||
<module>
|
||||
<web>
|
||||
<web-uri>bsa</web-uri>
|
||||
<context-root>bsa</context-root>
|
||||
</web>
|
||||
</module>
|
||||
<module>
|
||||
<web>
|
||||
<web-uri>tools</web-uri>
|
||||
|
||||
17
core/src/main/java/google/registry/env/common/bsa/WEB-INF/logging.properties
vendored
Normal file
17
core/src/main/java/google/registry/env/common/bsa/WEB-INF/logging.properties
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# A default java.util.logging configuration.
|
||||
# (All App Engine logging is through java.util.logging by default).
|
||||
#
|
||||
# To use this configuration, copy it into your application's WEB-INF
|
||||
# folder and add the following to your appengine-web.xml:
|
||||
#
|
||||
# <system-properties>
|
||||
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
|
||||
# </system-properties>
|
||||
#
|
||||
|
||||
# Set the default logging level for all loggers to INFO.
|
||||
.level = INFO
|
||||
|
||||
# Turn off logging in Hibernate classes for misleading ERROR-level logs
|
||||
org.hibernate.engine.jdbc.batch.internal.BatchingBatch.level=OFF
|
||||
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.level=OFF
|
||||
70
core/src/main/java/google/registry/env/common/bsa/WEB-INF/web.xml
vendored
Normal file
70
core/src/main/java/google/registry/env/common/bsa/WEB-INF/web.xml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
|
||||
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
<!-- Servlets -->
|
||||
|
||||
<!-- Servlet for injected backends actions -->
|
||||
<servlet>
|
||||
<display-name>BsaServlet</display-name>
|
||||
<servlet-name>bsa-servlet</servlet-name>
|
||||
<servlet-class>google.registry.module.bsa.BsaServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<!-- Test action -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/bsa</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Security config -->
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Internal</web-resource-name>
|
||||
<description>
|
||||
Admin-only internal section. Requests for paths covered by the URL patterns below will be
|
||||
checked for a logged-in user account that's allowed to access the AppEngine admin console
|
||||
(NOTE: this includes Editor/Viewer permissions in addition to Owner and the new IAM
|
||||
App Engine Admin role. See https://cloud.google.com/appengine/docs/java/access-control
|
||||
specifically the "Access handlers that have a login:admin restriction" line.)
|
||||
|
||||
TODO(b/28219927): lift some of these restrictions so that we can allow OAuth authentication
|
||||
for endpoints that need to be accessed by open-source automated processes.
|
||||
</description>
|
||||
|
||||
<!-- Internal AppEngine endpoints. The '_ah' is short for app hosting. -->
|
||||
<url-pattern>/_ah/*</url-pattern>
|
||||
|
||||
<!-- Registrar console (should not be available on non-default module). -->
|
||||
<url-pattern>/registrar*</url-pattern>
|
||||
|
||||
<!-- Verbatim JavaScript sources (only visible to admins for debugging). -->
|
||||
<url-pattern>/assets/sources/*</url-pattern>
|
||||
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>admin</role-name>
|
||||
</auth-constraint>
|
||||
|
||||
<!-- Repeated here since catch-all rule below is not inherited. -->
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<!-- Require TLS on all requests. -->
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Secure</web-resource-name>
|
||||
<description>
|
||||
Require encryption for all paths. http URLs will be redirected to https.
|
||||
</description>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
</web-app>
|
||||
30
core/src/main/java/google/registry/env/crash/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
30
core/src/main/java/google/registry/env/crash/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>10</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="crash"/>
|
||||
</system-properties>
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-crash/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1m"/>
|
||||
</static-files>
|
||||
</appengine-web-app>
|
||||
28
core/src/main/java/google/registry/env/local/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
28
core/src/main/java/google/registry/env/local/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>10</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="local"/>
|
||||
<property name="appengine.generated.dir"
|
||||
value="/tmp/domain-registry-appengine-generated/local/"/>
|
||||
</system-properties>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
</static-files>
|
||||
</appengine-web-app>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>backend</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
30
core/src/main/java/google/registry/env/production/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
30
core/src/main/java/google/registry/env/production/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<service>bsa</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>100</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="production"/>
|
||||
</system-properties>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
<static-error-handlers>
|
||||
<handler file="error.html"/>
|
||||
</static-error-handlers>
|
||||
</appengine-web-app>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>default</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>pubapi</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>tools</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
35
core/src/main/java/google/registry/env/qa/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
35
core/src/main/java/google/registry/env/qa/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<service>bsa</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>10</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="qa"/>
|
||||
</system-properties>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1h"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-qa/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
<static-error-handlers>
|
||||
<handler file="error.html"/>
|
||||
</static-error-handlers>
|
||||
</appengine-web-app>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>backend</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
@@ -22,12 +23,6 @@
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-sandbox/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
<static-error-handlers>
|
||||
<handler file="error.html"/>
|
||||
|
||||
36
core/src/main/java/google/registry/env/sandbox/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
36
core/src/main/java/google/registry/env/sandbox/bsa/WEB-INF/appengine-web.xml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<service>bsa</service>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
<max-instances>100</max-instances>
|
||||
<idle-timeout>10m</idle-timeout>
|
||||
</basic-scaling>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file"
|
||||
value="WEB-INF/logging.properties"/>
|
||||
<property name="google.registry.environment"
|
||||
value="sandbox"/>
|
||||
</system-properties>
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-sandbox/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
<static-error-handlers>
|
||||
<handler file="error.html"/>
|
||||
</static-error-handlers>
|
||||
</appengine-web-app>
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>default</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>pubapi</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java17</runtime>
|
||||
<runtime>java8</runtime>
|
||||
<service>tools</service>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<!--app-engine-apis>true</app-engine-apis-->
|
||||
<threadsafe>true</threadsafe>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -138,6 +138,7 @@ public final class GmailClient {
|
||||
BodyPart attachmentPart = new MimeBodyPart();
|
||||
attachmentPart.setContent(attachment.content(), attachment.contentType().toString());
|
||||
attachmentPart.setFileName(attachment.filename());
|
||||
attachmentPart.setDisposition(MimeBodyPart.ATTACHMENT);
|
||||
multipart.addBodyPart(attachmentPart);
|
||||
}
|
||||
msg.addRecipients(RecipientType.BCC, toArray(emailMessage.bccs(), Address.class));
|
||||
|
||||
@@ -550,6 +550,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
@JsonSerialize(using = SortedEnumSetSerializer.class)
|
||||
Set<IdnTableEnum> idnTables;
|
||||
|
||||
// TODO(11/30/2023): uncomment below two lines
|
||||
// /** The start time of this TLD's enrollment in the BSA program, if applicable. */
|
||||
// @JsonIgnore @Nullable DateTime bsaEnrollStartTime;
|
||||
|
||||
public String getTldStr() {
|
||||
return tldStr;
|
||||
}
|
||||
@@ -569,6 +573,15 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return tldType;
|
||||
}
|
||||
|
||||
/** Returns the time when this TLD was enrolled in the Brand Safety Alliance (BSA) program. */
|
||||
@JsonIgnore // Annotation can be removed once we add the field and annotate it.
|
||||
@Nullable
|
||||
public DateTime getBsaEnrollStartTime() {
|
||||
// TODO(11/30/2023): uncomment below.
|
||||
// return this.bsaEnrollStartTime;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Retrieve whether invoicing is enabled. */
|
||||
public boolean isInvoicingEnabled() {
|
||||
return invoicingEnabled;
|
||||
@@ -939,6 +952,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
public Builder setReservedListsByName(Set<String> reservedListNames) {
|
||||
// TODO(b/309175133): forbid if enrolled with BSA
|
||||
checkArgument(reservedListNames != null, "reservedListNames must not be null");
|
||||
ImmutableSet.Builder<ReservedList> builder = new ImmutableSet.Builder<>();
|
||||
for (String reservedListName : reservedListNames) {
|
||||
@@ -958,6 +972,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
public Builder setReservedLists(Set<ReservedList> reservedLists) {
|
||||
// TODO(b/309175133): forbid if enrolled with BSA
|
||||
checkArgumentNotNull(reservedLists, "reservedLists must not be null");
|
||||
ImmutableSet.Builder<String> nameBuilder = new ImmutableSet.Builder<>();
|
||||
for (ReservedList reservedList : reservedLists) {
|
||||
@@ -1076,6 +1091,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
}
|
||||
|
||||
public Builder setIdnTables(ImmutableSet<IdnTableEnum> idnTables) {
|
||||
// TODO(b/309175133): forbid if enrolled with BSA.
|
||||
getInstance().idnTables = idnTables;
|
||||
return this;
|
||||
}
|
||||
@@ -1085,6 +1101,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBsaEnrollStartTime(DateTime enrollTime) {
|
||||
// TODO(b/309175133): forbid if enrolled with BSA
|
||||
// TODO(11/30/2023): uncomment below line
|
||||
// getInstance().bsaEnrollStartTime = enrollTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tld build() {
|
||||
final Tld instance = getInstance();
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2023 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.module.bsa;
|
||||
|
||||
import com.google.monitoring.metrics.MetricReporter;
|
||||
import dagger.Component;
|
||||
import dagger.Lazy;
|
||||
import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.module.bsa.BsaRequestComponent.BsaRequestComponentModule;
|
||||
import google.registry.monitoring.whitebox.StackdriverModule;
|
||||
import google.registry.request.Modules.GsonModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.request.auth.AuthModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
@Component(
|
||||
modules = {
|
||||
AuthModule.class,
|
||||
UtilsModule.class,
|
||||
UserServiceModule.class,
|
||||
GsonModule.class,
|
||||
ConfigModule.class,
|
||||
StackdriverModule.class,
|
||||
CredentialModule.class,
|
||||
BsaRequestComponentModule.class
|
||||
})
|
||||
interface BsaComponent {
|
||||
BsaRequestHandler requestHandler();
|
||||
|
||||
Lazy<MetricReporter> metricReporter();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2023 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.module.bsa;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Subcomponent;
|
||||
import google.registry.bsa.PlaceholderAction;
|
||||
import google.registry.request.RequestComponentBuilder;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
|
||||
@RequestScope
|
||||
@Subcomponent(
|
||||
modules = {
|
||||
RequestModule.class,
|
||||
})
|
||||
interface BsaRequestComponent {
|
||||
|
||||
PlaceholderAction bsaAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<BsaRequestComponent> {
|
||||
|
||||
@Override
|
||||
public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@Override
|
||||
public abstract BsaRequestComponent build();
|
||||
}
|
||||
|
||||
@Module(subcomponents = BsaRequestComponent.class)
|
||||
class BsaRequestComponentModule {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2023 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.module.bsa;
|
||||
|
||||
import google.registry.request.RequestHandler;
|
||||
import google.registry.request.auth.RequestAuthenticator;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
public class BsaRequestHandler extends RequestHandler<BsaRequestComponent> {
|
||||
@Inject
|
||||
public BsaRequestHandler(
|
||||
Provider<BsaRequestComponent.Builder> componentBuilderProvider,
|
||||
RequestAuthenticator requestAuthenticator) {
|
||||
super(componentBuilderProvider, requestAuthenticator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 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.module.bsa;
|
||||
|
||||
import com.google.monitoring.metrics.MetricReporter;
|
||||
import dagger.Lazy;
|
||||
import google.registry.module.ServletBase;
|
||||
|
||||
public final class BsaServlet extends ServletBase {
|
||||
|
||||
private static final BsaComponent component = DaggerBsaComponent.create();
|
||||
private static final BsaRequestHandler requestHandler = component.requestHandler();
|
||||
private static final Lazy<MetricReporter> metricReporter = component.metricReporter();
|
||||
|
||||
public BsaServlet() {
|
||||
super(requestHandler, metricReporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
@javax.annotation.ParametersAreNonnullByDefault
|
||||
package google.registry.module.bsa;
|
||||
@@ -39,7 +39,7 @@ import javax.persistence.PersistenceException;
|
||||
* <p>See the {@code logging.properties} files in the {@code env} package for the specific Hibernate
|
||||
* classes that have logging suppressed.
|
||||
*/
|
||||
class DatabaseException extends PersistenceException {
|
||||
public class DatabaseException extends PersistenceException {
|
||||
|
||||
private transient String cachedMessage;
|
||||
|
||||
|
||||
@@ -30,11 +30,13 @@ public @interface Action {
|
||||
|
||||
/** App Engine services supported by the request processor. */
|
||||
enum Service {
|
||||
BSA("bsa"),
|
||||
DEFAULT("default"),
|
||||
TOOLS("tools"),
|
||||
BACKEND("backend"),
|
||||
PUBAPI("pubapi");
|
||||
|
||||
|
||||
private final String serviceId;
|
||||
|
||||
Service(String serviceId) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.request;
|
||||
|
||||
import com.google.common.net.MediaType;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -51,4 +52,11 @@ public interface Response {
|
||||
* @see HttpServletResponse#setDateHeader(String, long)
|
||||
*/
|
||||
void setDateHeader(String header, DateTime timestamp);
|
||||
|
||||
/**
|
||||
* Adds a cookie to the response
|
||||
*
|
||||
* @see HttpServletResponse#addCookie(Cookie)
|
||||
*/
|
||||
void addCookie(Cookie cookie);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.request;
|
||||
import com.google.common.net.MediaType;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -58,4 +59,9 @@ public final class ResponseImpl implements Response {
|
||||
public void setDateHeader(String header, DateTime timestamp) {
|
||||
rsp.setDateHeader(header, timestamp.getMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCookie(Cookie cookie) {
|
||||
rsp.addCookie(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ public class AuthModule {
|
||||
@Qualifier
|
||||
@interface RegularOidc {}
|
||||
|
||||
@Qualifier
|
||||
@interface RegularOidcFallback {}
|
||||
|
||||
@Provides
|
||||
@IapOidc
|
||||
@Singleton
|
||||
@@ -71,6 +74,14 @@ public class AuthModule {
|
||||
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RegularOidcFallback
|
||||
@Singleton
|
||||
TokenVerifier provideFallbackRegularTokenVerifier(
|
||||
@Config("fallbackOauthClientId") String clientId) {
|
||||
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IapOidc
|
||||
@Singleton
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IapOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidcFallback;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -53,6 +54,8 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
|
||||
protected final TokenVerifier tokenVerifier;
|
||||
|
||||
protected final Optional<TokenVerifier> fallbackTokenVerifier;
|
||||
|
||||
protected final TokenExtractor tokenExtractor;
|
||||
|
||||
private final ImmutableSet<String> serviceAccountEmails;
|
||||
@@ -60,9 +63,11 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
protected OidcTokenAuthenticationMechanism(
|
||||
ImmutableSet<String> serviceAccountEmails,
|
||||
TokenVerifier tokenVerifier,
|
||||
@Nullable TokenVerifier fallbackTokenVerifier,
|
||||
TokenExtractor tokenExtractor) {
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
this.fallbackTokenVerifier = Optional.ofNullable(fallbackTokenVerifier);
|
||||
this.tokenExtractor = tokenExtractor;
|
||||
}
|
||||
|
||||
@@ -77,7 +82,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
JsonWebSignature token = null;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
@@ -86,8 +91,25 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
|
||||
? "Raw token redacted in prod"
|
||||
: rawIdToken);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
if (fallbackTokenVerifier.isPresent()) {
|
||||
try {
|
||||
token = fallbackTokenVerifier.get().verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log(
|
||||
"Failed OIDC fallback verification attempt:\n%s",
|
||||
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
|
||||
? "Raw token redacted in prod"
|
||||
: rawIdToken);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
} else {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
}
|
||||
|
||||
String email = (String) token.getPayload().get("email");
|
||||
if (email == null) {
|
||||
logger.atWarning().log("No email address from the OIDC token:\n%s", token.getPayload());
|
||||
@@ -141,7 +163,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@IapOidc TokenVerifier tokenVerifier,
|
||||
@IapOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
super(serviceAccountEmails, tokenVerifier, null, tokenExtractor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +183,9 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
protected RegularOidcAuthenticationMechanism(
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@RegularOidc TokenVerifier tokenVerifier,
|
||||
@RegularOidcFallback TokenVerifier fallbackTokenVerifier,
|
||||
@RegularOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
super(serviceAccountEmails, tokenVerifier, fallbackTokenVerifier, tokenExtractor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.joda.time.Duration;
|
||||
/** Helper class for generating and validate XSRF tokens. */
|
||||
public final class XsrfTokenManager {
|
||||
|
||||
/** HTTP header used for transmitting XSRF tokens. */
|
||||
/** HTTP header or cookie name used for transmitting XSRF tokens. */
|
||||
public static final String X_CSRF_TOKEN = "X-CSRF-Token";
|
||||
|
||||
/** POST parameter used for transmitting XSRF tokens. */
|
||||
|
||||
@@ -38,9 +38,7 @@ public final class IdnLabelValidator {
|
||||
public Optional<String> findValidIdnTableForTld(String label, String tldStr) {
|
||||
String unicodeString = Idn.toUnicode(label);
|
||||
Tld tld = Tld.get(tldStr); // uses the cache
|
||||
ImmutableSet<IdnTableEnum> idnTablesForTld = tld.getIdnTables();
|
||||
ImmutableSet<IdnTableEnum> idnTables =
|
||||
idnTablesForTld.isEmpty() ? DEFAULT_IDN_TABLES : idnTablesForTld;
|
||||
ImmutableSet<IdnTableEnum> idnTables = getIdnTablesForTld(tld);
|
||||
for (IdnTableEnum idnTable : idnTables) {
|
||||
if (idnTable.getTable().isValidLabel(unicodeString)) {
|
||||
return Optional.of(idnTable.getTable().getName());
|
||||
@@ -48,4 +46,10 @@ public final class IdnLabelValidator {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Returns the names of the IDN tables supported by a {@code tld}. */
|
||||
public ImmutableSet<IdnTableEnum> getIdnTablesForTld(Tld tld) {
|
||||
ImmutableSet<IdnTableEnum> idnTablesForTld = tld.getIdnTables();
|
||||
return idnTablesForTld.isEmpty() ? DEFAULT_IDN_TABLES : idnTablesForTld;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public final class IdnTable {
|
||||
* Returns true if the given label is valid for this IDN table. A label is considered valid if all
|
||||
* of its codepoints are in the IDN table.
|
||||
*/
|
||||
boolean isValidLabel(String label) {
|
||||
public boolean isValidLabel(String label) {
|
||||
final int length = label.length();
|
||||
for (int i = 0; i < length; ) {
|
||||
int codepoint = label.codePointAt(i);
|
||||
|
||||
@@ -170,6 +170,8 @@ public class ServiceConnection {
|
||||
return RegistryConfig.getToolsServer();
|
||||
case BACKEND:
|
||||
return RegistryConfig.getBackendServer();
|
||||
case BSA:
|
||||
return RegistryConfig.getBsaServer();
|
||||
case PUBAPI:
|
||||
return RegistryConfig.getPubapiServer();
|
||||
}
|
||||
|
||||
@@ -58,13 +58,25 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/** The number of DNS updates to enqueue per transaction. */
|
||||
private static final int DEFAULT_BATCH_SIZE = 250;
|
||||
|
||||
/**
|
||||
* The default number of DNS updates it is safe to execute per minute.
|
||||
*
|
||||
* <p>This is mostly a guess based on existing system performance, but the point is to be on the
|
||||
* safe side and not cause contention with ongoing DNS updates from clients.
|
||||
*/
|
||||
private static final int DEFAULT_REFRESH_QPS = 7;
|
||||
|
||||
private final Response response;
|
||||
private final ImmutableSet<String> tlds;
|
||||
|
||||
// Recommended value for batch size is between 200 and 500
|
||||
private final int batchSize;
|
||||
|
||||
private final int refreshQps;
|
||||
|
||||
private final Random random;
|
||||
|
||||
@Inject
|
||||
@@ -72,10 +84,12 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
Response response,
|
||||
@Parameter(PARAM_TLDS) ImmutableSet<String> tlds,
|
||||
@Parameter("batchSize") Optional<Integer> batchSize,
|
||||
@Parameter("refreshQps") Optional<Integer> refreshQps,
|
||||
Random random) {
|
||||
this.response = response;
|
||||
this.tlds = tlds;
|
||||
this.batchSize = batchSize.orElse(DEFAULT_BATCH_SIZE);
|
||||
this.refreshQps = refreshQps.orElse(DEFAULT_REFRESH_QPS);
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@@ -83,7 +97,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
public void run() {
|
||||
assertTldsExist(tlds);
|
||||
checkArgument(batchSize > 0, "Must specify a positive number for batch size");
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes, TRANSACTION_REPEATABLE_READ);
|
||||
Duration smear = tm().transact(this::calculateSmear, TRANSACTION_REPEATABLE_READ);
|
||||
|
||||
ImmutableList<String> domainsBatch;
|
||||
@Nullable String lastInPreviousBatch = null;
|
||||
@@ -91,17 +105,16 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
Optional<String> lastInPreviousBatchOpt = Optional.ofNullable(lastInPreviousBatch);
|
||||
domainsBatch =
|
||||
tm().transact(
|
||||
() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes),
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
() -> refreshBatch(lastInPreviousBatchOpt, smear), TRANSACTION_REPEATABLE_READ);
|
||||
lastInPreviousBatch = domainsBatch.isEmpty() ? null : getLast(domainsBatch);
|
||||
} while (domainsBatch.size() == batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of smear minutes to enqueue refreshes so that the DNS queue does not get
|
||||
* Calculates the smear duration to enqueue refreshes so that the DNS queue does not get
|
||||
* overloaded.
|
||||
*/
|
||||
private int calculateSmearMinutes() {
|
||||
private Duration calculateSmear() {
|
||||
Long activeDomains =
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM Domain WHERE tld IN (:tlds) AND deletionTime = :endOfTime",
|
||||
@@ -109,7 +122,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
.setParameter("tlds", tlds)
|
||||
.setParameter("endOfTime", END_OF_TIME)
|
||||
.getSingleResult();
|
||||
return Math.max(activeDomains.intValue() / 1000, 1);
|
||||
return Duration.standardSeconds(Math.max(activeDomains / refreshQps, 1));
|
||||
}
|
||||
|
||||
private ImmutableList<String> getBatch(Optional<String> lastInPreviousBatch) {
|
||||
@@ -127,11 +140,12 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ImmutableList<String> refreshBatch(Optional<String> lastInPreviousBatch, int smearMinutes) {
|
||||
ImmutableList<String> refreshBatch(Optional<String> lastInPreviousBatch, Duration smear) {
|
||||
ImmutableList<String> domainBatch = getBatch(lastInPreviousBatch);
|
||||
try {
|
||||
// Smear the task execution time over the next N minutes.
|
||||
requestDomainDnsRefresh(domainBatch, Duration.standardMinutes(random.nextInt(smearMinutes)));
|
||||
// Smear the task execution time over the next N seconds.
|
||||
requestDomainDnsRefresh(
|
||||
domainBatch, Duration.standardSeconds(random.nextInt((int) smear.getStandardSeconds())));
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log("Error while enqueuing DNS refresh batch");
|
||||
response.setStatus(HttpStatus.SC_OK);
|
||||
|
||||
@@ -81,4 +81,10 @@ public class ToolsServerModule {
|
||||
static Optional<Integer> provideBatchSize(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "batchSize");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("refreshQps")
|
||||
static Optional<Integer> provideRefreshQps(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "refreshQps");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
/** Base class for handling Console API requests */
|
||||
public abstract class ConsoleApiAction implements Runnable {
|
||||
protected ConsoleApiParams consoleApiParams;
|
||||
|
||||
public ConsoleApiAction(ConsoleApiParams consoleApiParams) {
|
||||
this.consoleApiParams = consoleApiParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
// Shouldn't be even possible because of Auth annotations on the various implementing classes
|
||||
if (!consoleApiParams.authResult().userAuthInfo().get().consoleUser().isPresent()) {
|
||||
consoleApiParams.response().setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
User user = consoleApiParams.authResult().userAuthInfo().get().consoleUser().get();
|
||||
if (consoleApiParams.request().getMethod().equals(GET.toString())) {
|
||||
getHandler(user);
|
||||
} else {
|
||||
if (verifyXSRF()) {
|
||||
postHandler(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void postHandler(User user) {
|
||||
throw new UnsupportedOperationException("Console API POST handler not implemented");
|
||||
}
|
||||
|
||||
protected void getHandler(User user) {
|
||||
throw new UnsupportedOperationException("Console API GET handler not implemented");
|
||||
}
|
||||
|
||||
private boolean verifyXSRF() {
|
||||
Optional<Cookie> maybeCookie =
|
||||
Arrays.stream(consoleApiParams.request().getCookies())
|
||||
.filter(c -> XsrfTokenManager.X_CSRF_TOKEN.equals(c.getName()))
|
||||
.findFirst();
|
||||
if (!maybeCookie.isPresent()
|
||||
|| !consoleApiParams.xsrfTokenManager().validateToken(maybeCookie.get().getValue())) {
|
||||
consoleApiParams.response().setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,10 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.Cookie;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@Action(
|
||||
@@ -34,12 +32,10 @@ import org.json.JSONObject;
|
||||
path = ConsoleUserDataAction.PATH,
|
||||
method = {GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleUserDataAction implements JsonGetAction {
|
||||
public class ConsoleUserDataAction extends ConsoleApiAction {
|
||||
|
||||
public static final String PATH = "/console-api/userdata";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final String productName;
|
||||
private final String supportPhoneNumber;
|
||||
private final String supportEmail;
|
||||
@@ -47,14 +43,12 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
|
||||
@Inject
|
||||
public ConsoleUserDataAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Config("productName") String productName,
|
||||
@Config("supportEmail") String supportEmail,
|
||||
@Config("supportPhoneNumber") String supportPhoneNumber,
|
||||
@Config("technicalDocsUrl") String technicalDocsUrl) {
|
||||
this.response = response;
|
||||
this.authResult = authResult;
|
||||
super(consoleApiParams);
|
||||
this.productName = productName;
|
||||
this.supportEmail = supportEmail;
|
||||
this.supportPhoneNumber = supportPhoneNumber;
|
||||
@@ -62,13 +56,15 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
UserAuthInfo authInfo = authResult.userAuthInfo().get();
|
||||
if (!authInfo.consoleUser().isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
User user = authInfo.consoleUser().get();
|
||||
protected void getHandler(User user) {
|
||||
// As this is a first GET request we use it as an opportunity to set a XSRF cookie
|
||||
// for angular to read - https://angular.io/guide/http-security-xsrf-protection
|
||||
Cookie xsrfCookie =
|
||||
new Cookie(
|
||||
consoleApiParams.xsrfTokenManager().X_CSRF_TOKEN,
|
||||
consoleApiParams.xsrfTokenManager().generateToken(user.getEmailAddress()));
|
||||
xsrfCookie.setSecure(true);
|
||||
consoleApiParams.response().addCookie(xsrfCookie);
|
||||
|
||||
JSONObject json =
|
||||
new JSONObject(
|
||||
@@ -90,7 +86,7 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
// Is used by UI to construct a link to registry resources
|
||||
"technicalDocsUrl", technicalDocsUrl));
|
||||
|
||||
response.setPayload(json.toString());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
consoleApiParams.response().setPayload(json.toString());
|
||||
consoleApiParams.response().setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2023 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 com.google.auto.value.AutoValue;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/** Groups necessary dependencies for Console API actions * */
|
||||
@AutoValue
|
||||
public abstract class ConsoleApiParams {
|
||||
public static ConsoleApiParams create(
|
||||
HttpServletRequest request,
|
||||
Response response,
|
||||
AuthResult authResult,
|
||||
XsrfTokenManager xsrfTokenManager) {
|
||||
return new AutoValue_ConsoleApiParams(request, response, authResult, xsrfTokenManager);
|
||||
}
|
||||
|
||||
public abstract HttpServletRequest request();
|
||||
|
||||
public abstract Response response();
|
||||
|
||||
public abstract AuthResult authResult();
|
||||
|
||||
public abstract XsrfTokenManager xsrfTokenManager();
|
||||
}
|
||||
@@ -28,6 +28,10 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.request.OptionalJsonPayload;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -35,9 +39,18 @@ import org.joda.time.DateTime;
|
||||
/** Dagger module for the Registrar Console parameters. */
|
||||
@Module
|
||||
public final class RegistrarConsoleModule {
|
||||
|
||||
static final String PARAM_CLIENT_ID = "clientId";
|
||||
|
||||
@Provides
|
||||
@RequestScope
|
||||
ConsoleApiParams provideConsoleApiParams(
|
||||
HttpServletRequest request,
|
||||
Response response,
|
||||
AuthResult authResult,
|
||||
XsrfTokenManager xsrfTokenManager) {
|
||||
return ConsoleApiParams.create(request, response, authResult, xsrfTokenManager);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_CLIENT_ID)
|
||||
static Optional<String> provideOptionalClientId(HttpServletRequest req) {
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
|
||||
<mapping-file>META-INF/orm.xml</mapping-file>
|
||||
|
||||
<class>google.registry.bsa.persistence.BsaDownload</class>
|
||||
<class>google.registry.bsa.persistence.BsaLabel</class>
|
||||
<class>google.registry.bsa.persistence.BsaDomainInUse</class>
|
||||
<class>google.registry.model.billing.BillingCancellation</class>
|
||||
<class>google.registry.model.billing.BillingEvent</class>
|
||||
<class>google.registry.model.billing.BillingRecurrence</class>
|
||||
|
||||
94
core/src/test/java/google/registry/bsa/IdnCheckerTest.java
Normal file
94
core/src/test/java/google/registry/bsa/IdnCheckerTest.java
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2023 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.bsa;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.tldconfig.idn.IdnTableEnum.EXTENDED_LATIN;
|
||||
import static google.registry.tldconfig.idn.IdnTableEnum.JA;
|
||||
import static google.registry.tldconfig.idn.IdnTableEnum.UNCONFUSABLE_LATIN;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.tld.Tld;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class IdnCheckerTest {
|
||||
|
||||
@Mock Tld jaonly;
|
||||
@Mock Tld jandelatin;
|
||||
@Mock Tld strictlatin;
|
||||
IdnChecker idnChecker;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
idnChecker =
|
||||
new IdnChecker(
|
||||
ImmutableMap.of(
|
||||
JA,
|
||||
ImmutableSet.of(jandelatin, jaonly),
|
||||
EXTENDED_LATIN,
|
||||
ImmutableSet.of(jandelatin),
|
||||
UNCONFUSABLE_LATIN,
|
||||
ImmutableSet.of(strictlatin)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllValidIdns_allTlds() {
|
||||
assertThat(idnChecker.getAllValidIdns("all"))
|
||||
.containsExactly(EXTENDED_LATIN, JA, UNCONFUSABLE_LATIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllValidIdns_notJa() {
|
||||
assertThat(idnChecker.getAllValidIdns("à")).containsExactly(EXTENDED_LATIN, UNCONFUSABLE_LATIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllValidIdns_extendedLatinOnly() {
|
||||
assertThat(idnChecker.getAllValidIdns("á")).containsExactly(EXTENDED_LATIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllValidIdns_jaOnly() {
|
||||
assertThat(idnChecker.getAllValidIdns("っ")).containsExactly(JA);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllValidIdns_none() {
|
||||
assertThat(idnChecker.getAllValidIdns("д")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSupportingTlds_singleTld_success() {
|
||||
assertThat(idnChecker.getSupportingTlds(ImmutableSet.of("EXTENDED_LATIN")))
|
||||
.containsExactly(jandelatin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSupportingTlds_multiTld_success() {
|
||||
assertThat(idnChecker.getSupportingTlds(ImmutableSet.of("JA")))
|
||||
.containsExactly(jandelatin, jaonly);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getForbiddingTlds_success() {
|
||||
assertThat(idnChecker.getForbiddingTlds(ImmutableSet.of("JA"))).containsExactly(strictlatin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.bsa.persistence.BsaDomainInUse.Reason;
|
||||
import google.registry.persistence.transaction.DatabaseException;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link BsaDomainInUse}. */
|
||||
public class BsaDomainInUseTest {
|
||||
|
||||
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
@Test
|
||||
void persist() {
|
||||
tm().transact(() -> tm().put(new BsaLabel("label", fakeClock.nowUtc())));
|
||||
tm().transact(() -> tm().put(new BsaDomainInUse("label", "tld", Reason.REGISTERED)));
|
||||
BsaDomainInUse persisted =
|
||||
tm().transact(() -> tm().loadByKey(BsaDomainInUse.vKey("label", "tld")));
|
||||
assertThat(persisted.label).isEqualTo("label");
|
||||
assertThat(persisted.tld).isEqualTo("tld");
|
||||
assertThat(persisted.reason).isEqualTo(Reason.REGISTERED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cascadeDeletion() {
|
||||
tm().transact(() -> tm().put(new BsaLabel("label", fakeClock.nowUtc())));
|
||||
tm().transact(() -> tm().put(new BsaDomainInUse("label", "tld", Reason.REGISTERED)));
|
||||
assertThat(tm().transact(() -> tm().loadByKeyIfPresent(BsaDomainInUse.vKey("label", "tld"))))
|
||||
.isPresent();
|
||||
tm().transact(() -> tm().delete(BsaLabel.vKey("label")));
|
||||
assertThat(tm().transact(() -> tm().loadByKeyIfPresent(BsaDomainInUse.vKey("label", "tld"))))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void insertDomainWithoutLabel() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
DatabaseException.class,
|
||||
() ->
|
||||
tm().transact(
|
||||
() -> tm().put(new BsaDomainInUse("label", "tld", Reason.REGISTERED)))))
|
||||
.hasMessageThat()
|
||||
.contains("violates foreign key constraint");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.bsa.BlockList.BLOCK;
|
||||
import static google.registry.bsa.BlockList.BLOCK_PLUS;
|
||||
import static google.registry.bsa.DownloadStage.DOWNLOAD;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.bsa.BlockList;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit test for {@link BsaDownload}. */
|
||||
public class BsaDownloadTest {
|
||||
|
||||
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
@Test
|
||||
void saveJob() {
|
||||
BsaDownload persisted = tm().transact(() -> tm().getEntityManager().merge(new BsaDownload()));
|
||||
assertThat(persisted.jobId).isNotNull();
|
||||
assertThat(persisted.creationTime.getTimestamp()).isEqualTo(fakeClock.nowUtc());
|
||||
assertThat(persisted.stage).isEqualTo(DOWNLOAD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadJobByKey() {
|
||||
BsaDownload persisted = tm().transact(() -> tm().getEntityManager().merge(new BsaDownload()));
|
||||
assertThat(tm().transact(() -> tm().loadByKey(BsaDownload.vKey(persisted.jobId))))
|
||||
.isEqualTo(persisted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void checksums() {
|
||||
BsaDownload job = new BsaDownload();
|
||||
assertThat(job.getChecksums()).isEmpty();
|
||||
ImmutableMap<BlockList, String> checksums = ImmutableMap.of(BLOCK, "a", BLOCK_PLUS, "b");
|
||||
job.setBlockListChecksums(checksums);
|
||||
assertThat(job.getChecksums()).isEqualTo(checksums);
|
||||
assertThat(job.blockListChecksums).isEqualTo("BLOCK=a,BLOCK_PLUS=b");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2023 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.bsa.persistence;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link BsaLabel}. */
|
||||
public class BsaLabelTest {
|
||||
|
||||
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
@Test
|
||||
void persist() {
|
||||
tm().transact(() -> tm().put(new BsaLabel("label", fakeClock.nowUtc())));
|
||||
BsaLabel persisted = tm().transact(() -> tm().loadByKey(BsaLabel.vKey("label")));
|
||||
assertThat(persisted.getLabel()).isEqualTo("label");
|
||||
assertThat(persisted.creationTime).isEqualTo(fakeClock.nowUtc());
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,7 @@ public class GmailClientTest {
|
||||
assertThat(attachment.getContentType()).startsWith(CSV_UTF_8.toString());
|
||||
assertThat(attachment.getContentType()).endsWith("name=filename");
|
||||
assertThat(attachment.getContent()).isEqualTo("foo,bar\nbaz,qux");
|
||||
assertThat(attachment.getDisposition()).isEqualTo("attachment");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2023 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.module.bsa;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class BsaServletTest {
|
||||
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
|
||||
|
||||
@Test
|
||||
void testService_unknownPath_returnsNotFound() throws Exception {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/lol");
|
||||
new BsaServlet().service(req, rsp);
|
||||
verify(rsp).sendError(404);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.request.auth.AuthModule.BEARER_PREFIX;
|
||||
import static google.registry.request.auth.AuthModule.IAP_HEADER_NAME;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -70,7 +69,7 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
|
||||
private AuthResult authResult;
|
||||
private OidcTokenAuthenticationMechanism authenticationMechanism =
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, e -> rawToken) {};
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, null, e -> rawToken) {};
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaTestExtensions.JpaUnitTestExtension jpaExtension =
|
||||
@@ -78,7 +77,7 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
when(tokenVerifier.verify(eq(rawToken))).thenReturn(jwt);
|
||||
when(tokenVerifier.verify(rawToken)).thenReturn(jwt);
|
||||
payload.setEmail(email);
|
||||
payload.setSubject(gaiaId);
|
||||
insertInDb(user);
|
||||
@@ -98,18 +97,30 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
@Test
|
||||
void testAuthenticate_noTokenFromRequest() {
|
||||
authenticationMechanism =
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, e -> null) {};
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, null, e -> null) {};
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_invalidToken() throws Exception {
|
||||
when(tokenVerifier.verify(eq(rawToken))).thenThrow(new VerificationException("Bad token"));
|
||||
when(tokenVerifier.verify(rawToken)).thenThrow(new VerificationException("Bad token"));
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_fallbackVerifier() throws Exception {
|
||||
TokenVerifier fallbackVerifier = mock(TokenVerifier.class);
|
||||
when(tokenVerifier.verify(rawToken)).thenThrow(new VerificationException("Bad token"));
|
||||
when(fallbackVerifier.verify(rawToken)).thenReturn(jwt);
|
||||
authenticationMechanism =
|
||||
new OidcTokenAuthenticationMechanism(
|
||||
serviceAccounts, tokenVerifier, fallbackVerifier, e -> rawToken) {};
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_noEmailAddress() throws Exception {
|
||||
payload.setEmail(null);
|
||||
@@ -223,5 +234,12 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
String provideOauthClientId() {
|
||||
return "client-id";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("fallbackOauthClientId")
|
||||
String provideFallbackOauthClientId() {
|
||||
return "fallback-client-id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ package google.registry.schema.integration;
|
||||
|
||||
import static com.google.common.truth.Truth.assert_;
|
||||
|
||||
import google.registry.bsa.persistence.BsaDomainInUseTest;
|
||||
import google.registry.bsa.persistence.BsaDownloadTest;
|
||||
import google.registry.bsa.persistence.BsaLabelTest;
|
||||
import google.registry.model.billing.BillingBaseTest;
|
||||
import google.registry.model.common.CursorTest;
|
||||
import google.registry.model.common.DnsRefreshRequestTest;
|
||||
@@ -82,6 +85,9 @@ import org.junit.runner.RunWith;
|
||||
BeforeSuiteTest.class,
|
||||
AllocationTokenTest.class,
|
||||
BillingBaseTest.class,
|
||||
BsaDomainInUseTest.class,
|
||||
BsaDownloadTest.class,
|
||||
BsaLabelTest.class,
|
||||
BulkPricingPackageTest.class,
|
||||
ClaimsListDaoTest.class,
|
||||
ContactHistoryTest.class,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2023 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.testing;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
public final class FakeConsoleApiParams {
|
||||
|
||||
public static ConsoleApiParams get(Optional<AuthResult> maybeAuthResult) {
|
||||
AuthResult authResult =
|
||||
maybeAuthResult.orElseGet(
|
||||
() ->
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new com.google.appengine.api.users.User(
|
||||
"JohnDoe@theregistrar.com", "theregistrar.com"),
|
||||
false)));
|
||||
return ConsoleApiParams.create(
|
||||
mock(HttpServletRequest.class),
|
||||
new FakeResponse(),
|
||||
authResult,
|
||||
new XsrfTokenManager(
|
||||
new FakeClock(DateTime.parse("2020-02-02T01:23:45Z")), mock(UserService.class)));
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,10 @@ import static java.util.Collections.unmodifiableMap;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.request.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.Cookie;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Fake implementation of {@link Response} for testing. */
|
||||
@@ -36,6 +38,8 @@ public final class FakeResponse implements Response {
|
||||
private boolean wasMutuallyExclusiveResponseSet;
|
||||
private String lastResponseStackTrace;
|
||||
|
||||
private ArrayList<Cookie> cookies = new ArrayList<>();
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
@@ -83,6 +87,15 @@ public final class FakeResponse implements Response {
|
||||
headers.put(checkNotNull(header), checkNotNull(timestamp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCookie(Cookie cookie) {
|
||||
cookies.add(cookie);
|
||||
}
|
||||
|
||||
public ArrayList<Cookie> getCookies() {
|
||||
return cookies;
|
||||
}
|
||||
|
||||
private void checkResponsePerformedOnce() {
|
||||
checkState(
|
||||
!wasMutuallyExclusiveResponseSet,
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
|
||||
package google.registry.tldconfig.idn;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -118,4 +121,24 @@ class IdnLabelValidatorTest {
|
||||
// Extended Latin shouldn't include Japanese characters
|
||||
assertThat(idnLabelValidator.findValidIdnTableForTld("みんな", "tld")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetIdnTablesForTld_custom() {
|
||||
persistResource(
|
||||
createTld("tld")
|
||||
.asBuilder()
|
||||
.setIdnTables(ImmutableSet.of(IdnTableEnum.EXTENDED_LATIN))
|
||||
.build());
|
||||
Tld tld = tm().transact(() -> tm().loadByKey(Tld.createVKey("tld")));
|
||||
assertThat(idnLabelValidator.getIdnTablesForTld(tld))
|
||||
.containsExactly(IdnTableEnum.EXTENDED_LATIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetIdnTablesForTld_default() {
|
||||
persistResource(createTld("tld").asBuilder().build());
|
||||
Tld tld = tm().transact(() -> tm().loadByKey(Tld.createVKey("tld")));
|
||||
assertThat(idnLabelValidator.getIdnTablesForTld(tld))
|
||||
.containsExactly(IdnTableEnum.EXTENDED_LATIN, IdnTableEnum.JA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EntityYamlUtils.createObjectMapper;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -47,6 +48,8 @@ import google.registry.model.tld.label.PremiumListDao;
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -98,6 +101,20 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
|
||||
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
|
||||
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
|
||||
assertThat(updatedTld.getBreakglassMode()).isFalse();
|
||||
assertThat(tld.getBsaEnrollStartTime()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_updateTld_bsaTimeUnaffected() throws Exception {
|
||||
Tld tld = createTld("tld");
|
||||
DateTime bsaStartTime = DateTime.now(DateTimeZone.UTC);
|
||||
tm().transact(() -> tm().put(tld.asBuilder().setBsaEnrollStartTime(bsaStartTime).build()));
|
||||
File tldFile = tmpDir.resolve("tld.yaml").toFile();
|
||||
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
|
||||
runCommandForced("--input=" + tldFile);
|
||||
// TODO(11/30/2023): uncomment below two lines
|
||||
// Tld updatedTld = Tld.get("tld");
|
||||
// assertThat(tld.getBsaEnrollStartTime()).isEqualTo(bsaStartTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -35,6 +35,7 @@ import google.registry.testing.FakeResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -55,7 +56,7 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
createTld("bar");
|
||||
action =
|
||||
new RefreshDnsForAllDomainsAction(
|
||||
response, ImmutableSet.of("bar"), Optional.of(10), new Random());
|
||||
response, ImmutableSet.of("bar"), Optional.of(10), Optional.empty(), new Random());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,9 +75,9 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
// Set batch size to 1 since each batch will be enqueud at the same time
|
||||
action =
|
||||
new RefreshDnsForAllDomainsAction(
|
||||
response, ImmutableSet.of("bar"), Optional.of(1), new Random());
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), 1000));
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), 1000));
|
||||
response, ImmutableSet.of("bar"), Optional.of(1), Optional.of(7), new Random());
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), Duration.standardMinutes(1000)));
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), Duration.standardMinutes(1000)));
|
||||
ImmutableList<DnsRefreshRequest> refreshRequests =
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.registrar.HtmlAction;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import io.github.classgraph.ClassGraph;
|
||||
@@ -34,6 +35,7 @@ final class ActionMembershipTest {
|
||||
// 1. Extending HtmlAction to signal that we are serving an HTML page
|
||||
// 2. Extending JsonAction to show that we are serving JSON POST requests
|
||||
// 3. Extending JsonGetAction to serve JSON GET requests
|
||||
// 4. Extending ConsoleApiAction to serve JSON requests
|
||||
ImmutableSet.Builder<String> failingClasses = new ImmutableSet.Builder<>();
|
||||
try (ScanResult scanResult =
|
||||
new ClassGraph().enableAnnotationInfo().whitelistPackages("google.registry.ui").scan()) {
|
||||
@@ -41,7 +43,8 @@ final class ActionMembershipTest {
|
||||
.getClassesWithAnnotation(Action.class.getName())
|
||||
.forEach(
|
||||
classInfo -> {
|
||||
if (!classInfo.extendsSuperclass(HtmlAction.class.getName())
|
||||
if (!classInfo.extendsSuperclass(ConsoleApiAction.class.getName())
|
||||
&& !classInfo.extendsSuperclass(HtmlAction.class.getName())
|
||||
&& !classInfo.implementsInterface(JsonActionRunner.JsonAction.class.getName())
|
||||
&& !classInfo.implementsInterface(JsonGetAction.class.getName())) {
|
||||
failingClasses.add(classInfo.getName());
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
@@ -22,12 +24,18 @@ import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeConsoleApiParams;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.Cookie;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@@ -35,12 +43,31 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
class ConsoleUserDataActionTest {
|
||||
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private FakeResponse response = new FakeResponse();
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@Test
|
||||
void testSuccess_hasXSRFCookie() throws IOException {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
|
||||
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(user));
|
||||
ConsoleUserDataAction action =
|
||||
createAction(
|
||||
Optional.of(FakeConsoleApiParams.get(Optional.of(authResult))), Action.Method.GET);
|
||||
action.run();
|
||||
ArrayList<Cookie> cookies = ((FakeResponse) consoleApiParams.response()).getCookies();
|
||||
assertThat(cookies.stream().map(cookie -> cookie.getName()).collect(toImmutableList()))
|
||||
.containsExactly("X-CSRF-Token");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getContactInfo() throws IOException {
|
||||
User user =
|
||||
@@ -49,10 +76,15 @@ class ConsoleUserDataActionTest {
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
|
||||
ConsoleUserDataAction action = createAction(AuthResult.createUser(UserAuthInfo.create(user)));
|
||||
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(user));
|
||||
ConsoleUserDataAction action =
|
||||
createAction(
|
||||
Optional.of(FakeConsoleApiParams.get(Optional.of(authResult))), Action.Method.GET);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Map jsonObject = GSON.fromJson(response.getPayload(), Map.class);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus())
|
||||
.isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Map jsonObject =
|
||||
GSON.fromJson(((FakeResponse) consoleApiParams.response()).getPayload(), Map.class);
|
||||
assertThat(jsonObject)
|
||||
.containsExactly(
|
||||
"isAdmin",
|
||||
@@ -71,19 +103,18 @@ class ConsoleUserDataActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_notAConsoleUser() throws IOException {
|
||||
ConsoleUserDataAction action =
|
||||
createAction(
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new com.google.appengine.api.users.User(
|
||||
"JohnDoe@theregistrar.com", "theregistrar.com"),
|
||||
false)));
|
||||
ConsoleUserDataAction action = createAction(Optional.empty(), Action.Method.GET);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus())
|
||||
.isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
private ConsoleUserDataAction createAction(AuthResult authResult) throws IOException {
|
||||
private ConsoleUserDataAction createAction(
|
||||
Optional<ConsoleApiParams> maybeConsoleApiParams, Action.Method method) throws IOException {
|
||||
consoleApiParams =
|
||||
maybeConsoleApiParams.orElseGet(() -> FakeConsoleApiParams.get(Optional.empty()));
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(method.toString());
|
||||
return new ConsoleUserDataAction(
|
||||
authResult, response, "Nomulus", "support@example.com", "+1 (212) 867 5309", "test");
|
||||
consoleApiParams, "Nomulus", "support@example.com", "+1 (212) 867 5309", "test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,11 +261,11 @@ td.section {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2023-11-02 18:26:18.901466</td>
|
||||
<td class="property_value">2023-11-09 01:49:59.861801</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
<td id="lastFlywayFile" class="property_value">V149__add_bsa_domain_in_use_table.sql</td>
|
||||
<td id="lastFlywayFile" class="property_value">V150__add_tld_bsa_enroll_date.sql</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -285,7 +285,7 @@ td.section {
|
||||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="3835.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2023-11-02 18:26:18.901466
|
||||
2023-11-09 01:49:59.861801
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3748,-4 3748,-44 4013,-44 4013,-4 3748,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -147,3 +147,4 @@ V146__last_update_time_via_epp.sql
|
||||
V147__drop_gaia_id_from_user.sql
|
||||
V148__add_bsa_download_and_label_tables.sql
|
||||
V149__add_bsa_domain_in_use_table.sql
|
||||
V150__add_tld_bsa_enroll_date.sql
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Copyright 2023 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.
|
||||
|
||||
ALTER TABLE public."Tld" ADD COLUMN bsa_enroll_start_time timestamptz;
|
||||
@@ -85,6 +85,29 @@
|
||||
primary key (billing_recurrence_id)
|
||||
);
|
||||
|
||||
create table "BsaDomainInUse" (
|
||||
label text not null,
|
||||
tld text not null,
|
||||
creation_time timestamptz not null,
|
||||
reason text not null,
|
||||
primary key (label, tld)
|
||||
);
|
||||
|
||||
create table "BsaDownload" (
|
||||
job_id bigserial not null,
|
||||
block_list_checksums text not null,
|
||||
creation_time timestamptz not null,
|
||||
stage text not null,
|
||||
update_timestamp timestamptz,
|
||||
primary key (job_id)
|
||||
);
|
||||
|
||||
create table "BsaLabel" (
|
||||
label text not null,
|
||||
creation_time timestamptz not null,
|
||||
primary key (label)
|
||||
);
|
||||
|
||||
create table "ClaimsEntry" (
|
||||
revision_id int8 not null,
|
||||
domain_label text not null,
|
||||
@@ -785,6 +808,7 @@ create index IDXoqttafcywwdn41um6kwlt0n8b on "BillingRecurrence" (domain_repo_id
|
||||
create index IDXp3usbtvk0v1m14i5tdp4xnxgc on "BillingRecurrence" (recurrence_end_time);
|
||||
create index IDXp0pxi708hlu4n40qhbtihge8x on "BillingRecurrence" (recurrence_last_expansion);
|
||||
create index IDXjny8wuot75b5e6p38r47wdawu on "BillingRecurrence" (recurrence_time_of_year);
|
||||
create index IDXj874kw19bgdnkxo1rue45jwlw on "BsaDownload" (creation_time);
|
||||
create index IDX3y752kr9uh4kh6uig54vemx0l on "Contact" (creation_time);
|
||||
create index IDXtm415d6fe1rr35stm33s5mg18 on "Contact" (current_sponsor_registrar_id);
|
||||
create index IDXn1f711wicdnooa2mqb7g1m55o on "Contact" (deletion_time);
|
||||
|
||||
@@ -1146,7 +1146,8 @@ CREATE TABLE public."Tld" (
|
||||
dns_ds_ttl interval,
|
||||
dns_ns_ttl interval,
|
||||
idn_tables text[],
|
||||
breakglass_mode boolean DEFAULT false NOT NULL
|
||||
breakglass_mode boolean DEFAULT false NOT NULL,
|
||||
bsa_enroll_start_time timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ sized to support not just the normal ongoing DNS load but also the load incurred
|
||||
by MapReduces, both scheduled (such as RDE) and on-demand (asynchronous
|
||||
contact/host deletion).
|
||||
|
||||
#### BSA service
|
||||
|
||||
The bsa service is responsible for business logic behind Nomulus and BSA
|
||||
functionality. Requests to the backend service are handled by the `BsaServlet`,
|
||||
which provides all of the endpoints exposed in `BsaRequestComponent`. These
|
||||
include tasks for downloading, processing and uploading BSA data.
|
||||
|
||||
|
||||
#### Tools service
|
||||
|
||||
The tools service is responsible for servicing requests from the `nomulus`
|
||||
|
||||
@@ -46,7 +46,7 @@ else
|
||||
-PmavenUrl="${gcs_prefix}"/maven \
|
||||
-PpluginsUrl="${gcs_prefix}"/plugins
|
||||
|
||||
for service in default pubapi backend tools
|
||||
for service in default pubapi backend bsa tools
|
||||
do
|
||||
mv services/"${service}"/build/staged-app "${dest}/${service}"
|
||||
done
|
||||
|
||||
@@ -43,7 +43,7 @@ steps:
|
||||
|
||||
gcloud auth activate-service-account --key-file=tool-credential.json
|
||||
|
||||
for service in default pubapi backend tools
|
||||
for service in default pubapi backend bsa tools
|
||||
do
|
||||
for version in $(gcloud app versions list \
|
||||
--filter="SERVICE:$service AND SERVING_STATUS:STOPPED" \
|
||||
|
||||
@@ -75,7 +75,7 @@ steps:
|
||||
gcloud app versions list \
|
||||
--project $project_id --hide-no-traffic \
|
||||
--format="csv[no-heading](SERVICE,VERSION.ID)" | \
|
||||
grep -e "^backend\|^default\|^pubapi\|^tools" |\
|
||||
grep -e "^backend\|^default\|^bsa\|^pubapi\|^tools" |\
|
||||
while read line; do echo "${TAG_NAME},$line"; done | tee "$local_map"
|
||||
num_versions=$(cat "$local_map" | wc -l)
|
||||
if [ "$num_versions" -ne 4 ]; then
|
||||
|
||||
@@ -30,8 +30,7 @@ FROM gcr.io/${PROJECT_ID}/builder:${TAG_NAME}
|
||||
COPY deploy_sql_schema.sh /usr/local/bin/
|
||||
RUN \
|
||||
FLYWAY_MAVEN=https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline \
|
||||
&& FLYWAY_VERSION=$(curl ${FLYWAY_MAVEN}/maven-metadata.xml \
|
||||
| grep -oP "<release>\K.*(?=</release>)") \
|
||||
&& FLYWAY_VERSION="9.22.3" \
|
||||
&& echo "Downloading Flyway-commandline-${FLYWAY_VERSION}" \
|
||||
&& mkdir -p /flyway \
|
||||
&& curl -L ${FLYWAY_MAVEN}/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}.tar.gz \
|
||||
|
||||
@@ -43,6 +43,7 @@ include 'proxy'
|
||||
include 'util'
|
||||
include 'services:default'
|
||||
include 'services:backend'
|
||||
include 'services:bsa'
|
||||
include 'services:tools'
|
||||
include 'services:pubapi'
|
||||
include 'java8compatibility'
|
||||
|
||||
Reference in New Issue
Block a user