mirror of
https://github.com/google/nomulus
synced 2026-08-06 07:16:13 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74baae397a | ||
|
|
fddecea18e | ||
|
|
36a60bdf8b | ||
|
|
58ed53314c | ||
|
|
5eaf99e02a | ||
|
|
9a5f094d1d | ||
|
|
6cbc2fa5ef | ||
|
|
6883093735 | ||
|
|
a6078bc4f4 | ||
|
|
6b75cf8496 | ||
|
|
219e9d3afb |
+1
-1
@@ -103,7 +103,7 @@ nomulus.iws
|
||||
.gradle/
|
||||
**/build
|
||||
cloudbuild-caches/
|
||||
node_modules/**
|
||||
**/node_modules/**
|
||||
/repos/
|
||||
|
||||
# Compiled JS/CSS code
|
||||
|
||||
@@ -25,7 +25,7 @@ import textwrap
|
||||
import re
|
||||
|
||||
# We should never analyze any generated files
|
||||
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/", ".gradle/", "/dist/", "karma.conf.js", "polyfills.ts", "test.ts"}
|
||||
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/", ".gradle/", "/dist/", "karma.conf.js", "polyfills.ts", "test.ts", "/docs/console-endpoints/"}
|
||||
# We can't rely on CI to have the Enum package installed so we do this instead.
|
||||
FORBIDDEN = 1
|
||||
REQUIRED = 2
|
||||
|
||||
Generated
+6
-6
@@ -10909,9 +10909,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
|
||||
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz",
|
||||
"integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
@@ -20541,9 +20541,9 @@
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
|
||||
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz",
|
||||
"integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
|
||||
@@ -21,7 +21,7 @@ import static google.registry.batch.BatchModule.PARAM_DRY_RUN;
|
||||
import static google.registry.config.RegistryEnvironment.PRODUCTION;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
|
||||
@@ -43,7 +43,7 @@ import google.registry.rde.RdeMarshaller;
|
||||
import google.registry.rde.RdeModule;
|
||||
import google.registry.rde.RdeResourceType;
|
||||
import google.registry.rde.RdeUploadAction;
|
||||
import google.registry.rde.RdeUtil;
|
||||
import google.registry.rde.RdeUtils;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.RequestParameters;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
@@ -166,7 +166,7 @@ public class RdeIO {
|
||||
final int revision =
|
||||
Optional.ofNullable(key.revision())
|
||||
.orElseGet(() -> RdeRevision.getNextRevision(tld, watermark, mode));
|
||||
String id = RdeUtil.timestampToId(watermark);
|
||||
String id = RdeUtils.timestampToId(watermark);
|
||||
String prefix =
|
||||
options.getJobName()
|
||||
+ '/'
|
||||
|
||||
@@ -27,9 +27,9 @@ import static google.registry.cron.CronModule.FOR_EACH_TEST_TLD_PARAM;
|
||||
import static google.registry.cron.CronModule.JITTER_SECONDS_PARAM;
|
||||
import static google.registry.cron.CronModule.QUEUE_PARAM;
|
||||
import static google.registry.cron.CronModule.RUN_IN_EMPTY_PARAM;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.model.tld.Tld.TldType.REAL;
|
||||
import static google.registry.model.tld.Tld.TldType.TEST;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
|
||||
@@ -20,8 +20,8 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -38,7 +38,7 @@ public final class DnsUtils {
|
||||
private static void requestDnsRefresh(String name, TargetType type, Duration delay) {
|
||||
// Throws an IllegalArgumentException if the name is not under a managed TLD -- we only update
|
||||
// DNS for names that are under our management.
|
||||
String tld = Registries.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
String tld = Tlds.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().insert(
|
||||
@@ -114,7 +114,7 @@ public final class DnsUtils {
|
||||
}
|
||||
|
||||
public static long getDnsAPlusAAAATtlForHost(String host, Duration dnsDefaultATtl) {
|
||||
Optional<InternetDomainName> tldName = Registries.findTldForName(InternetDomainName.from(host));
|
||||
Optional<InternetDomainName> tldName = Tlds.findTldForName(InternetDomainName.from(host));
|
||||
Duration dnsAPlusAaaaTtl = dnsDefaultATtl;
|
||||
if (tldName.isPresent()) {
|
||||
Tld tld = Tld.get(tldName.get().toString());
|
||||
|
||||
@@ -40,8 +40,8 @@ import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.Concurrent;
|
||||
import google.registry.util.Retrier;
|
||||
@@ -248,7 +248,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
public void publishHost(String hostName) {
|
||||
// Get the superordinate domain name of the host.
|
||||
InternetDomainName host = InternetDomainName.from(hostName);
|
||||
Optional<InternetDomainName> tld = Registries.findTldForName(host);
|
||||
Optional<InternetDomainName> tld = Tlds.findTldForName(host);
|
||||
|
||||
// Host not managed by our registry, no need to update DNS.
|
||||
if (!tld.isPresent()) {
|
||||
|
||||
@@ -31,8 +31,8 @@ import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.util.Clock;
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
@@ -154,7 +154,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
// Get the superordinate domain name of the host.
|
||||
InternetDomainName host = InternetDomainName.from(hostName);
|
||||
ImmutableList<String> hostParts = host.parts();
|
||||
Optional<InternetDomainName> tld = Registries.findTldForName(host);
|
||||
Optional<InternetDomainName> tld = Tlds.findTldForName(host);
|
||||
|
||||
// host not managed by our registry, no need to update DNS.
|
||||
if (!tld.isPresent()) {
|
||||
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- TODO: @ptkach - Delete once Cloud Api deployer is up and running -->
|
||||
<queue-entries>
|
||||
|
||||
<!-- Queue for reading DNS update requests and batching them off to the dns-publish queue. -->
|
||||
<queue>
|
||||
<name>dns-refresh</name>
|
||||
<rate>100/s</rate>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for publishing DNS updates in batches. -->
|
||||
<queue>
|
||||
<name>dns-publish</name>
|
||||
<rate>100/s</rate>
|
||||
<bucket-size>100</bucket-size>
|
||||
<!-- 30 sec backoff increasing linearly up to 30 minutes. -->
|
||||
<retry-parameters>
|
||||
<min-backoff-seconds>30</min-backoff-seconds>
|
||||
<max-backoff-seconds>1800</max-backoff-seconds>
|
||||
<max-doublings>0</max-doublings>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE deposits to the escrow provider. -->
|
||||
<queue>
|
||||
<name>rde-upload</name>
|
||||
<rate>10/m</rate>
|
||||
<bucket-size>50</bucket-size>
|
||||
<max-concurrent-requests>5</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-age-limit>4h</task-age-limit>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE reports to ICANN. -->
|
||||
<queue>
|
||||
<name>rde-report</name>
|
||||
<rate>1/s</rate>
|
||||
<max-concurrent-requests>1</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-age-limit>4h</task-age-limit>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for copying BRDA deposits to GCS. -->
|
||||
<queue>
|
||||
<name>brda</name>
|
||||
<rate>1/m</rate>
|
||||
<max-concurrent-requests>10</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-age-limit>23h</task-age-limit>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that trigger domain DNS update upon host rename. -->
|
||||
<queue>
|
||||
<name>async-host-rename</name>
|
||||
<rate>1/s</rate>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that wait for a Beam pipeline to complete (i.e. Spec11 and invoicing). -->
|
||||
<queue>
|
||||
<name>beam-reporting</name>
|
||||
<rate>1/m</rate>
|
||||
<max-concurrent-requests>1</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-retry-limit>5</task-retry-limit>
|
||||
<min-backoff-seconds>180</min-backoff-seconds>
|
||||
<max-backoff-seconds>180</max-backoff-seconds>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that communicate with TMCH MarksDB webserver. -->
|
||||
<queue>
|
||||
<name>marksdb</name>
|
||||
<rate>1/m</rate>
|
||||
<max-concurrent-requests>1</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-age-limit>11h</task-age-limit> <!-- cron interval minus hour -->
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks to produce LORDN CSV reports, populated by a Cloud Scheduler fanout job. -->
|
||||
<queue>
|
||||
<name>nordn</name>
|
||||
<rate>1/s</rate>
|
||||
<max-concurrent-requests>10</max-concurrent-requests>
|
||||
<retry-parameters>
|
||||
<task-age-limit>11h</task-age-limit> <!-- cron interval minus hour -->
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that sync data to Google Spreadsheets. -->
|
||||
<queue>
|
||||
<name>sheet</name>
|
||||
<rate>1/s</rate>
|
||||
<!-- max-concurrent-requests is intentionally omitted. -->
|
||||
<retry-parameters>
|
||||
<task-age-limit>1h</task-age-limit>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for infrequent cron tasks (i.e. hourly or less often) that should retry three times on failure. -->
|
||||
<queue>
|
||||
<name>retryable-cron-tasks</name>
|
||||
<rate>1/s</rate>
|
||||
<retry-parameters>
|
||||
<task-retry-limit>3</task-retry-limit>
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for async actions that should be run at some point in the future. -->
|
||||
<queue>
|
||||
<name>async-actions</name>
|
||||
<rate>1/s</rate>
|
||||
<max-concurrent-requests>5</max-concurrent-requests>
|
||||
</queue>
|
||||
|
||||
</queue-entries>
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.export;
|
||||
|
||||
import static com.google.common.base.Verify.verifyNotNull;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -26,12 +26,12 @@ import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static google.registry.model.tld.label.ReservationType.ALLOWED_IN_SUNRISE;
|
||||
import static google.registry.model.tld.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.flows.host;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.isActive;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
|
||||
@@ -403,7 +403,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
public static ImmutableMap<VKey<? extends EppResource>, EppResource> loadCached(
|
||||
Iterable<VKey<? extends EppResource>> keys) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().loadByKeys(keys);
|
||||
return tm().transact(() -> tm().loadByKeys(keys));
|
||||
}
|
||||
return ImmutableMap.copyOf(cacheEppResources.getAll(keys));
|
||||
}
|
||||
@@ -416,7 +416,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*/
|
||||
public static <T extends EppResource> T loadCached(VKey<T> key) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().loadByKey(key);
|
||||
return tm().transact(() -> tm().loadByKey(key));
|
||||
}
|
||||
// Safe to cast because loading a Key<T> returns an entity of type T.
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -16,6 +16,10 @@ package google.registry.model.console;
|
||||
|
||||
/** Permissions that users may have in the UI, either per-registrar or globally. */
|
||||
public enum ConsolePermission {
|
||||
/** View basic information about a registrar. */
|
||||
VIEW_REGISTRAR_DETAILS,
|
||||
/** Edit basic information about a registrar. */
|
||||
EDIT_REGISTRAR_DETAILS,
|
||||
/** Add, update, or remove other console users. */
|
||||
MANAGE_USERS,
|
||||
/** Add, update, or remove registrars. */
|
||||
|
||||
@@ -27,6 +27,8 @@ public class ConsoleRoleDefinitions {
|
||||
/** Permissions for a registry support agent. */
|
||||
static final ImmutableSet<ConsolePermission> SUPPORT_AGENT_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.VIEW_REGISTRAR_DETAILS,
|
||||
ConsolePermission.EDIT_REGISTRAR_DETAILS,
|
||||
ConsolePermission.MANAGE_USERS,
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
@@ -69,6 +71,7 @@ public class ConsoleRoleDefinitions {
|
||||
/** Permissions for a registrar partner account manager. */
|
||||
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.VIEW_REGISTRAR_DETAILS,
|
||||
ConsolePermission.DOWNLOAD_DOMAINS,
|
||||
ConsolePermission.VIEW_TLD_PORTFOLIO,
|
||||
ConsolePermission.CONTACT_SUPPORT,
|
||||
@@ -89,6 +92,7 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.EDIT_REGISTRAR_DETAILS,
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
ConsolePermission.CHANGE_NOMULUS_PASSWORD,
|
||||
|
||||
@@ -26,7 +26,7 @@ import static com.google.common.collect.Sets.immutableEnumSet;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer;
|
||||
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
|
||||
|
||||
+5
-5
@@ -40,15 +40,15 @@ import java.util.stream.Stream;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/** Utilities for finding and listing {@link Tld} entities. */
|
||||
public final class Registries {
|
||||
public final class Tlds {
|
||||
|
||||
private Registries() {}
|
||||
private Tlds() {}
|
||||
|
||||
/** Supplier of a cached registries map. */
|
||||
/** Supplier of a cached TLDs map. */
|
||||
private static Supplier<ImmutableMap<String, TldType>> cache = createFreshCache();
|
||||
|
||||
/**
|
||||
* Returns a newly-created Supplier of a registries to types map.
|
||||
* Returns a newly-created Supplier of a TLDs to types map.
|
||||
*
|
||||
* <p>The supplier's get() method enters a transactionless context briefly to avoid enrolling the
|
||||
* query inside an unrelated client-affecting transaction.
|
||||
@@ -84,7 +84,7 @@ public final class Registries {
|
||||
return ImmutableSet.copyOf(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
}
|
||||
|
||||
/** Returns the Registry entities themselves of the given type loaded fresh from the database. */
|
||||
/** Returns the TLD entities themselves of the given type loaded fresh from the database. */
|
||||
public static ImmutableSet<Tld> getTldEntitiesOfType(TldType type) {
|
||||
return Tld.get(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.model.eppoutput.Result.Code;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -104,7 +104,7 @@ public abstract class EppMetric {
|
||||
String tld = Iterables.getOnlyElement(tlds);
|
||||
// Only record TLDs that actually exist, otherwise we can blow up cardinality by recording
|
||||
// an arbitrarily large number of strings.
|
||||
setTld(Optional.ofNullable(Registries.getTlds().contains(tld) ? tld : "_invalid"));
|
||||
setTld(Optional.ofNullable(Tlds.getTlds().contains(tld) ? tld : "_invalid"));
|
||||
break;
|
||||
default:
|
||||
setTld("_various");
|
||||
|
||||
@@ -112,8 +112,8 @@ final class ContactToXjcConverter {
|
||||
private static XjcRdeContactTransferDataType convertTransferData(TransferData model) {
|
||||
XjcRdeContactTransferDataType bean = new XjcRdeContactTransferDataType();
|
||||
bean.setTrStatus(XjcEppcomTrStatusType.fromValue(model.getTransferStatus().getXmlName()));
|
||||
bean.setReRr(RdeUtil.makeXjcRdeRrType(model.getGainingRegistrarId()));
|
||||
bean.setAcRr(RdeUtil.makeXjcRdeRrType(model.getLosingRegistrarId()));
|
||||
bean.setReRr(RdeUtils.makeXjcRdeRrType(model.getGainingRegistrarId()));
|
||||
bean.setAcRr(RdeUtils.makeXjcRdeRrType(model.getLosingRegistrarId()));
|
||||
bean.setReDate(model.getTransferRequestTime());
|
||||
bean.setAcDate(model.getPendingTransferExpirationTime());
|
||||
return bean;
|
||||
|
||||
@@ -262,8 +262,8 @@ final class DomainToXjcConverter {
|
||||
XjcRdeDomainTransferDataType bean = new XjcRdeDomainTransferDataType();
|
||||
bean.setTrStatus(
|
||||
XjcEppcomTrStatusType.fromValue(model.getTransferStatus().getXmlName()));
|
||||
bean.setReRr(RdeUtil.makeXjcRdeRrType(model.getGainingRegistrarId()));
|
||||
bean.setAcRr(RdeUtil.makeXjcRdeRrType(model.getLosingRegistrarId()));
|
||||
bean.setReRr(RdeUtils.makeXjcRdeRrType(model.getGainingRegistrarId()));
|
||||
bean.setAcRr(RdeUtils.makeXjcRdeRrType(model.getLosingRegistrarId()));
|
||||
bean.setReDate(model.getTransferRequestTime());
|
||||
bean.setAcDate(model.getPendingTransferExpirationTime());
|
||||
bean.setExDate(model.getTransferredRegistrationExpirationTime());
|
||||
|
||||
@@ -23,9 +23,9 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -83,7 +83,7 @@ public final class PendingDepositChecker {
|
||||
ImmutableSetMultimap.Builder<String, PendingDeposit> builder =
|
||||
new ImmutableSetMultimap.Builder<>();
|
||||
DateTime now = clock.nowUtc();
|
||||
for (String tldStr : Registries.getTldsOfType(TldType.REAL)) {
|
||||
for (String tldStr : Tlds.getTldsOfType(TldType.REAL)) {
|
||||
Tld tld = Tld.get(tldStr);
|
||||
if (!tld.getEscrowEnabled()) {
|
||||
continue;
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
|
||||
import static google.registry.model.rde.RdeMode.FULL;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.rde.RdeUtils.findMostRecentPrefixForWatermark;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
|
||||
@@ -98,8 +99,10 @@ public final class RdeReportAction implements Runnable, EscrowTask {
|
||||
RdeRevision.getCurrentRevision(tld, watermark, FULL)
|
||||
.orElseThrow(
|
||||
() -> new IllegalStateException("RdeRevision was not set on generated deposit"));
|
||||
String name =
|
||||
prefix.orElse("") + RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, revision);
|
||||
if (!prefix.isPresent()) {
|
||||
prefix = Optional.of(findMostRecentPrefixForWatermark(watermark, bucket, tld, gcsUtils));
|
||||
}
|
||||
String name = prefix.get() + RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, revision);
|
||||
BlobId reportFilename = BlobId.of(bucket, name + "-report.xml.ghostryde");
|
||||
verify(gcsUtils.existsAndNotEmpty(reportFilename), "Missing file: %s", reportFilename);
|
||||
reporter.send(readReportFromGcs(reportFilename));
|
||||
|
||||
@@ -43,6 +43,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -86,22 +87,23 @@ public class RdeReporter {
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
HTTPResponse rsp1 = urlFetchService.fetch(req);
|
||||
switch (rsp1.getResponseCode()) {
|
||||
case SC_OK:
|
||||
case SC_BAD_REQUEST:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("PUT failed");
|
||||
int responseCode = rsp1.getResponseCode();
|
||||
if (responseCode != SC_OK && responseCode != SC_BAD_REQUEST) {
|
||||
logger.atSevere().log(
|
||||
"Failure when trying to PUT RDE report to ICANN server: %d\n%s",
|
||||
responseCode, Arrays.toString(rsp1.getContent()));
|
||||
throw new RuntimeException("Error uploading deposits to ICANN");
|
||||
}
|
||||
return rsp1;
|
||||
},
|
||||
SocketTimeoutException.class);
|
||||
|
||||
// Ensure the XML response is valid.
|
||||
// Ensure the XML response is valid. The EPP result code would not be 1000 if we get an
|
||||
// SC_BAD_REQUEST as the HTTP response code.
|
||||
XjcIirdeaResult result = parseResult(rsp.getContent());
|
||||
if (result.getCode().getValue() != 1000) {
|
||||
logger.atWarning().log(
|
||||
"PUT rejected: %d %s\n%s",
|
||||
"Rejected when trying to PUT RDE report to ICANN server: %d %s\n%s",
|
||||
result.getCode().getValue(), result.getMsg(), result.getDescription());
|
||||
throw new InternalServerErrorException(result.getMsg());
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
|
||||
import static google.registry.model.rde.RdeMode.FULL;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.rde.RdeModule.RDE_REPORT_QUEUE;
|
||||
import static google.registry.rde.RdeUtils.findMostRecentPrefixForWatermark;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
@@ -31,7 +32,6 @@ import static java.util.Arrays.asList;
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.jcraft.jsch.JSch;
|
||||
@@ -136,26 +136,10 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
|
||||
@Override
|
||||
public void runWithLock(final DateTime watermark) throws Exception {
|
||||
// If a prefix is not provided, but we are in SQL mode, try to determine the prefix. This should
|
||||
// only happen when the RDE upload cron job runs to catch up any un-retried (i. e. expected)
|
||||
// RDE failures.
|
||||
// If a prefix is not provided,try to determine the prefix. This should only happen when the RDE
|
||||
// upload cron job runs to catch up any un-retried (i. e. expected) RDE failures.
|
||||
if (!prefix.isPresent()) {
|
||||
// The prefix is always in the format of: rde-2022-02-21t00-00-00z-2022-02-21t00-07-33z, where
|
||||
// the first datetime is the watermark and the second one is the time when the RDE beam job
|
||||
// launched. We search for the latest folder that starts with "rde-[watermark]".
|
||||
String partialPrefix =
|
||||
String.format("rde-%s", watermark.toString("yyyy-MM-dd't'HH-mm-ss'z'"));
|
||||
String latestFilenameSuffix =
|
||||
gcsUtils.listFolderObjects(bucket, partialPrefix).stream()
|
||||
.max(Ordering.natural())
|
||||
.orElse(null);
|
||||
if (latestFilenameSuffix == null) {
|
||||
throw new NoContentException(
|
||||
String.format("RDE deposit for TLD %s on %s does not exist", tld, watermark));
|
||||
}
|
||||
int firstSlashPosition = latestFilenameSuffix.indexOf('/');
|
||||
prefix =
|
||||
Optional.of(partialPrefix + latestFilenameSuffix.substring(0, firstSlashPosition + 1));
|
||||
prefix = Optional.of(findMostRecentPrefixForWatermark(watermark, bucket, tld, gcsUtils));
|
||||
}
|
||||
logger.atInfo().log("Verifying readiness to upload the RDE deposit.");
|
||||
Optional<Cursor> cursor =
|
||||
@@ -193,7 +177,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
() -> new IllegalStateException("RdeRevision was not set on generated deposit"));
|
||||
final String nameWithoutPrefix =
|
||||
RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, revision);
|
||||
final String name = prefix.orElse("") + nameWithoutPrefix;
|
||||
final String name = prefix.get() + nameWithoutPrefix;
|
||||
final BlobId xmlFilename = BlobId.of(bucket, name + ".xml.ghostryde");
|
||||
final BlobId xmlLengthFilename = BlobId.of(bucket, name + ".xml.length");
|
||||
BlobId reportFilename = BlobId.of(bucket, name + "-report.xml.ghostryde");
|
||||
|
||||
+31
-2
@@ -17,9 +17,12 @@ package google.registry.rde;
|
||||
import static google.registry.util.HexDumper.dumpHex;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.re2j.Matcher;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.request.HttpException.NoContentException;
|
||||
import google.registry.xjc.rde.XjcRdeRrType;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -31,7 +34,7 @@ import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
/** Helper methods for RDE. */
|
||||
public final class RdeUtil {
|
||||
public final class RdeUtils {
|
||||
|
||||
/** Number of bytes in head of XML deposit that will contain the information we want. */
|
||||
private static final int PEEK_SIZE = 2048;
|
||||
@@ -70,6 +73,32 @@ public final class RdeUtil {
|
||||
return DATETIME_FORMATTER.parseDateTime(watermarkMatcher.group(1));
|
||||
}
|
||||
|
||||
/** Find the most recent folder in the given GCS bucket for the given watermark. */
|
||||
public static String findMostRecentPrefixForWatermark(
|
||||
DateTime watermark, String bucket, String tld, GcsUtils gcsUtils) throws NoContentException {
|
||||
// The prefix is always in the format of: rde-2022-02-21t00-00-00z-2022-02-21t00-07-33z, where
|
||||
// the first datetime is the watermark and the second one is the time when the RDE beam job
|
||||
// launched. We search for the latest folder that starts with "rde-[watermark]".
|
||||
String partialPrefix = String.format("rde-%s", watermark.toString("yyyy-MM-dd't'HH-mm-ss'z'"));
|
||||
String latestFilenameSuffix = null;
|
||||
try {
|
||||
latestFilenameSuffix =
|
||||
gcsUtils.listFolderObjects(bucket, partialPrefix).stream()
|
||||
.max(Ordering.natural())
|
||||
.orElse(null);
|
||||
} catch (IOException e) {
|
||||
throw new NoContentException(
|
||||
String.format(
|
||||
"Error reading folders starting with %s in bucket %s", partialPrefix, bucket));
|
||||
}
|
||||
if (latestFilenameSuffix == null) {
|
||||
throw new NoContentException(
|
||||
String.format("RDE deposit for TLD %s on %s does not exist", tld, watermark));
|
||||
}
|
||||
int firstSlashPosition = latestFilenameSuffix.indexOf('/');
|
||||
return partialPrefix + latestFilenameSuffix.substring(0, firstSlashPosition + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an ID matching the regex {@code \w{1,13} } from a millisecond
|
||||
* timestamp.
|
||||
@@ -89,5 +118,5 @@ public final class RdeUtil {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private RdeUtil() {}
|
||||
private RdeUtils() {}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ package google.registry.reporting.icann;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.api.client.http.ByteArrayContent;
|
||||
|
||||
@@ -29,9 +29,9 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
@@ -203,7 +203,7 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
/** Returns a map of each cursor to the tld. */
|
||||
private ImmutableMap<Cursor, String> loadCursors() {
|
||||
|
||||
ImmutableSet<Tld> registries = Registries.getTldEntitiesOfType(TldType.REAL);
|
||||
ImmutableSet<Tld> registries = Tlds.getTldEntitiesOfType(TldType.REAL);
|
||||
|
||||
ImmutableMap<VKey<? extends Cursor>, Tld> activityKeyMap =
|
||||
loadKeyMap(registries, CursorType.ICANN_UPLOAD_ACTIVITY);
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.monitoring.metrics.EventMetric;
|
||||
import com.google.monitoring.metrics.LabelDescriptor;
|
||||
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -17,8 +17,8 @@ package google.registry.request;
|
||||
import static com.google.common.net.MediaType.JSON_UTF_8;
|
||||
import static google.registry.dns.PublishDnsUpdatesAction.APP_ENGINE_RETRY_HEADER;
|
||||
import static google.registry.dns.PublishDnsUpdatesAction.CLOUD_TASKS_RETRY_HEADER;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.request.RequestParameters.extractOptionalHeader;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static google.registry.request.RequestParameters.extractSetOfParameters;
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthMethod;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthSettings;
|
||||
import google.registry.request.auth.RequestAuthenticator.UserPolicy;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
|
||||
/** Enum used to configure authentication settings for Actions. */
|
||||
public enum Auth {
|
||||
@@ -25,21 +25,18 @@ public enum Auth {
|
||||
/**
|
||||
* Allows anyone access, doesn't attempt to authenticate user.
|
||||
*
|
||||
* Will never return absent(), but only authenticates access from App Engine task-queues. For
|
||||
* <p>Will never return absent(), but only authenticates access from App Engine task-queues. For
|
||||
* everyone else - returns NOT_AUTHENTICATED.
|
||||
*/
|
||||
AUTH_PUBLIC_ANONYMOUS(
|
||||
ImmutableList.of(AuthMethod.INTERNAL),
|
||||
AuthLevel.NONE,
|
||||
UserPolicy.PUBLIC),
|
||||
AUTH_PUBLIC_ANONYMOUS(ImmutableList.of(AuthMethod.INTERNAL), AuthLevel.NONE, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, does attempt to authenticate user.
|
||||
* Allows anyone to access, does attempt to authenticate user.
|
||||
*
|
||||
* If a user is logged in, will authenticate (and return) them. Otherwise, access is still
|
||||
* <p>If a user is logged in, will authenticate (and return) them. Otherwise, access is still
|
||||
* granted, but NOT_AUTHENTICATED is returned.
|
||||
*
|
||||
* Will never return absent().
|
||||
* <p>Will never return absent().
|
||||
*/
|
||||
AUTH_PUBLIC(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.LEGACY),
|
||||
@@ -47,17 +44,15 @@ public enum Auth {
|
||||
UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, as long as they are logged in.
|
||||
* Allows anyone to access, as long as they are logged in.
|
||||
*
|
||||
* Does not allow access from App Engine task-queues.
|
||||
* <p>Does not allow access from App Engine task-queues.
|
||||
*/
|
||||
AUTH_PUBLIC_LOGGED_IN(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.PUBLIC),
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, as long as they use OAuth to authenticate.
|
||||
* Allows anyone to access, as long as they use OAuth to authenticate.
|
||||
*
|
||||
* <p>Also allows access from App Engine task-queue. Note that OAuth client ID still needs to be
|
||||
* allow-listed in the config file for OAuth-based authentication to succeed.
|
||||
@@ -80,10 +75,7 @@ public enum Auth {
|
||||
|
||||
private final AuthSettings authSettings;
|
||||
|
||||
Auth(
|
||||
ImmutableList<AuthMethod> methods,
|
||||
AuthLevel minimumLevel,
|
||||
UserPolicy userPolicy) {
|
||||
Auth(ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
authSettings = AuthSettings.create(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2017 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.request.auth;
|
||||
|
||||
/**
|
||||
* Authentication level.
|
||||
*
|
||||
* <p>Used by {@link Auth} to specify what authentication is required, and by {@link AuthResult})
|
||||
* to specify what authentication was found. These are a series of levels, from least to most
|
||||
* authentication required. The lowest level of requirement, NONE, can be satisfied by any level
|
||||
* of authentication, while the highest level, USER, can only be satisfied by the authentication of
|
||||
* a specific user. The level returned may be higher than what was required, if more authentication
|
||||
* turns out to be possible. For instance, if an authenticated user is found, USER will be returned
|
||||
* even if no authentication was required.
|
||||
*/
|
||||
public enum AuthLevel {
|
||||
|
||||
/** No authentication was required/found. */
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Authentication required, but user not required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, but app-internal authentication (which isn't associated
|
||||
* with a specific user) is permitted.
|
||||
*
|
||||
* <p>In AuthResult: App-internal authentication was successful.
|
||||
*/
|
||||
APP,
|
||||
|
||||
/**
|
||||
* Authentication required, user required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, and app-internal authentication is forbidden, meaning
|
||||
* that a valid authentication result will contain specific user information.
|
||||
*
|
||||
* <p>In AuthResult: A valid user was authenticated.
|
||||
*/
|
||||
USER
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
import com.google.appengine.api.oauth.OAuthServiceFactory;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
@@ -21,35 +23,46 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.IapOidcAuthenticationMechanism;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.RegularOidcAuthenticationMechanism;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.TokenExtractor;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Dagger module for authentication routines.
|
||||
*/
|
||||
/** Dagger module for authentication routines. */
|
||||
@Module
|
||||
public class AuthModule {
|
||||
|
||||
// IAP-signed JWT will be in this header.
|
||||
// See https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers.
|
||||
public static final String IAP_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
// GAE will put the content in header "proxy-authorization" in this header when it routes the
|
||||
// request to the app.
|
||||
public static final String PROXY_HEADER_NAME = "X-Google-Proxy-Authorization";
|
||||
public static final String BEARER_PREFIX = "Bearer ";
|
||||
// TODO: Change the IAP audience format once we are on GKE.
|
||||
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
|
||||
private static final String IAP_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String SA_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth). */
|
||||
/** Provides the custom authentication mechanisms (including OAuth and OIDC). */
|
||||
@Provides
|
||||
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
|
||||
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism,
|
||||
ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism) {
|
||||
IapOidcAuthenticationMechanism iapOidcAuthenticationMechanism,
|
||||
RegularOidcAuthenticationMechanism regularOidcAuthenticationMechanism) {
|
||||
return ImmutableList.of(
|
||||
oauthAuthenticationMechanism,
|
||||
iapHeaderAuthenticationMechanism,
|
||||
serviceAccountAuthenticationMechanism);
|
||||
iapOidcAuthenticationMechanism,
|
||||
regularOidcAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@interface IAP {}
|
||||
@interface IapOidc {}
|
||||
|
||||
@Qualifier
|
||||
@interface ServiceAccount {}
|
||||
@interface RegularOidc {}
|
||||
|
||||
/** Provides the OAuthService instance. */
|
||||
@Provides
|
||||
@@ -58,18 +71,42 @@ public class AuthModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IAP
|
||||
@IapOidc
|
||||
@Singleton
|
||||
TokenVerifier provideTokenVerifier(
|
||||
TokenVerifier provideIapTokenVerifier(
|
||||
@Config("projectId") String projectId, @Config("projectIdNumber") long projectIdNumber) {
|
||||
String audience = String.format("/projects/%d/apps/%s", projectIdNumber, projectId);
|
||||
String audience = String.format(IAP_AUDIENCE_FORMAT, projectIdNumber, projectId);
|
||||
return TokenVerifier.newBuilder().setAudience(audience).setIssuer(IAP_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ServiceAccount
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenVerifier provideServiceAccountTokenVerifier(@Config("projectId") String projectId) {
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IapOidc
|
||||
@Singleton
|
||||
TokenExtractor provideIapTokenExtractor() {
|
||||
return request -> request.getHeader(IAP_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenExtractor provideRegularTokenExtractor() {
|
||||
return request -> {
|
||||
// TODO: only check the Authorizaiton header after the migration to OIDC is complete.
|
||||
String rawToken = request.getHeader(PROXY_HEADER_NAME);
|
||||
if (rawToken == null) {
|
||||
rawToken = request.getHeader(AUTHORIZATION);
|
||||
}
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.request.auth;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -66,6 +67,5 @@ public abstract class AuthResult {
|
||||
* returns NOT_AUTHENTICATED in this case, as opposed to absent() if authentication failed and was
|
||||
* required. So as a return from an authorization check, this can be treated as a success.
|
||||
*/
|
||||
public static final AuthResult NOT_AUTHENTICATED =
|
||||
AuthResult.create(AuthLevel.NONE);
|
||||
public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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.request.auth;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
|
||||
/**
|
||||
* Parameters used to configure the authenticator.
|
||||
*
|
||||
* <p>AuthSettings shouldn't be used directly, instead - use one of the predefined {@link Auth} enum
|
||||
* values.
|
||||
*/
|
||||
@Immutable
|
||||
@AutoValue
|
||||
public abstract class AuthSettings {
|
||||
|
||||
public abstract ImmutableList<AuthMethod> methods();
|
||||
|
||||
public abstract AuthLevel minimumLevel();
|
||||
|
||||
public abstract UserPolicy userPolicy();
|
||||
|
||||
static AuthSettings create(
|
||||
ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
return new AutoValue_AuthSettings(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
|
||||
/** Available methods for authentication. */
|
||||
public enum AuthMethod {
|
||||
|
||||
/** App Engine internal authentication. Must always be provided as the first method. */
|
||||
INTERNAL,
|
||||
|
||||
/** Authentication methods suitable for API-style access, such as OAuth 2. */
|
||||
API,
|
||||
|
||||
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
|
||||
LEGACY
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication level.
|
||||
*
|
||||
* <p>Used by {@link Auth} to specify what authentication is required, and by {@link AuthResult})
|
||||
* to specify what authentication was found. These are a series of levels, from least to most
|
||||
* authentication required. The lowest level of requirement, NONE, can be satisfied by any level
|
||||
* of authentication, while the highest level, USER, can only be satisfied by the authentication
|
||||
* of a specific user. The level returned may be higher than what was required, if more
|
||||
* authentication turns out to be possible. For instance, if an authenticated user is found, USER
|
||||
* will be returned even if no authentication was required.
|
||||
*/
|
||||
public enum AuthLevel {
|
||||
|
||||
/** No authentication was required/found. */
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Authentication required, but user not required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, but app-internal authentication (which isn't
|
||||
* associated with a specific user) is permitted.
|
||||
*
|
||||
* <p>In AuthResult: App-internal authentication was successful.
|
||||
*/
|
||||
APP,
|
||||
|
||||
/**
|
||||
* Authentication required, user required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, and app-internal authentication is forbidden, meaning
|
||||
* that a valid authentication result will contain specific user information.
|
||||
*
|
||||
* <p>In AuthResult: A valid user was authenticated.
|
||||
*/
|
||||
USER
|
||||
}
|
||||
|
||||
/** User authorization policy options. */
|
||||
public enum UserPolicy {
|
||||
|
||||
/** This action ignores end users; the only configured auth method must be INTERNAL. */
|
||||
IGNORED,
|
||||
|
||||
/** No user policy is enforced; anyone can access this action. */
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* If there is a user, it must be an admin, as determined by isUserAdmin().
|
||||
*
|
||||
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
|
||||
* including editors and viewers, is an admin.
|
||||
*/
|
||||
ADMIN
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IAP;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests that have gone through the GCP Identity-Aware Proxy.
|
||||
*
|
||||
* <p>When the user logs in, IAP provides a JWT in the <code>X-Goog-IAP-JWT-Assertion</code> header.
|
||||
* This header is included on all requests to IAP-enabled services (which should be all of them that
|
||||
* receive requests from the front end). The token verification libraries ensure that the signed
|
||||
* token has the proper audience and issuer.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
public class IapHeaderAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String ID_TOKEN_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
|
||||
@Inject
|
||||
public IapHeaderAuthenticationMechanism(@IAP TokenVerifier tokenVerifier) {
|
||||
super(tokenVerifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
return request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(emailAddress);
|
||||
if (!maybeUser.isPresent()) {
|
||||
logger.atInfo().log("No user found for email address %s", emailAddress);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public abstract class IdTokenAuthenticationBase implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
public IdTokenAuthenticationBase(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
}
|
||||
|
||||
abstract String rawTokenFromRequest(HttpServletRequest request);
|
||||
|
||||
abstract AuthResult authResultFromEmail(String email);
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = rawTokenFromRequest(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
return authResultFromEmail(emailAddress);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
import static google.registry.security.XsrfTokenManager.P_CSRF_TOKEN;
|
||||
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthModule.BEARER_PREFIX;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthRequestException;
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
@@ -35,8 +36,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*/
|
||||
public class OAuthAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final OAuthService oauthService;
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
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.AuthSettings.AuthLevel;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* An authenticam mechanism that verifies the OIDC token.
|
||||
*
|
||||
* <p>Currently, two flavors are supported: one that checkes for the OIDC token as a regular bearer
|
||||
* token, and another that checks for the OIDC token passed by IAP. In both cases, the {@link
|
||||
* AuthResult} with the highest {@link AuthLevel} possible is returned. So, if the email address for
|
||||
* which the token is minted exists both as a {@link User} and as a service account, the returned
|
||||
* {@link AuthResult} is at {@link AuthLevel#USER}.
|
||||
*
|
||||
* @see <a href="https://developers.google.com/identity/openid-connect/openid-connect">OpenID
|
||||
* Connect </a>
|
||||
*/
|
||||
public abstract class OidcTokenAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the OIDC authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static AuthResult authResultForTesting = null;
|
||||
|
||||
protected final TokenVerifier tokenVerifier;
|
||||
|
||||
protected final TokenExtractor tokenExtractor;
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
|
||||
protected OidcTokenAuthenticationMechanism(
|
||||
ImmutableList<String> serviceAccountEmails,
|
||||
TokenVerifier tokenVerifier,
|
||||
TokenExtractor tokenExtractor) {
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
this.tokenExtractor = tokenExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& authResultForTesting != null) {
|
||||
logger.atWarning().log("Using AuthResult %s for testing.", authResultForTesting);
|
||||
return authResultForTesting;
|
||||
}
|
||||
String rawIdToken = tokenExtractor.extract(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
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());
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.create(APP);
|
||||
}
|
||||
logger.atInfo().log("No service account found for email address %s", email);
|
||||
logger.atWarning().log(
|
||||
"The email address %s is not tied to a principal with access to Nomulus", email);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setAuthResultForTesting(@Nullable AuthResult authResult) {
|
||||
authResultForTesting = authResult;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void unsetAuthResultForTesting() {
|
||||
authResultForTesting = null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface TokenExtractor {
|
||||
@Nullable
|
||||
String extract(HttpServletRequest request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A mechanism to authenticate HTTP requests that have gone through the GCP Identity-Aware Proxy.
|
||||
*
|
||||
* <p>When the user logs in, IAP provides a JWT in the {@code X-Goog-IAP-JWT-Assertion} header.
|
||||
* This header is included on all requests to IAP-enabled services (which should be all of them
|
||||
* that receive requests from the front end). The token verification libraries ensure that the
|
||||
* signed token has the proper audience and issuer.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
static class IapOidcAuthenticationMechanism extends OidcTokenAuthenticationMechanism {
|
||||
|
||||
@Inject
|
||||
protected IapOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@IapOidc TokenVerifier tokenVerifier,
|
||||
@IapOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mechanism to authenticate HTTP requests with an OIDC token as a bearer token.
|
||||
*
|
||||
* <p>If the endpoint is not behind IAP, we can try to authenticate the OIDC token supplied in the
|
||||
* request header directly. Ideally we would like all endpoints to be behind IAP, but being able
|
||||
* to authenticate the token directly provides us with the flexibility to do away with OAuth-based
|
||||
* {@link OAuthAuthenticationMechanism} that is tied to App Engine runtime without having to turn
|
||||
* on IAP, which is an all-or-nothing switch for each GAE service (i.e. no way to turn it on only
|
||||
* for certain GAE endpoints).
|
||||
*
|
||||
* <p>Note that this mechanism will try to first extract the token under the "proxy-authorization"
|
||||
* header, before trying "authorization". This is because currently the GAE OAuth service always
|
||||
* uses "authorization", and we would like to provide a way for both auth mechanisms to be working
|
||||
* at the same time for the same request.
|
||||
*
|
||||
* @see <a href=https://datatracker.ietf.org/doc/html/rfc6750>Bearer Token Usage</a>
|
||||
*/
|
||||
static class RegularOidcAuthenticationMechanism extends OidcTokenAuthenticationMechanism {
|
||||
|
||||
@Inject
|
||||
protected RegularOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@RegularOidc TokenVerifier tokenVerifier,
|
||||
@RegularOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,12 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -44,57 +45,6 @@ public class RequestAuthenticator {
|
||||
this.legacyAuthenticationMechanism = legacyAuthenticationMechanism;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters used to configure the authenticator.
|
||||
*
|
||||
* AuthSettings shouldn't be used directly, instead - use one of the predefined {@link Auth} enum
|
||||
* values.
|
||||
*/
|
||||
@Immutable
|
||||
@AutoValue
|
||||
public abstract static class AuthSettings {
|
||||
|
||||
public abstract ImmutableList<AuthMethod> methods();
|
||||
public abstract AuthLevel minimumLevel();
|
||||
public abstract UserPolicy userPolicy();
|
||||
|
||||
static AuthSettings create(
|
||||
ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
return new AutoValue_RequestAuthenticator_AuthSettings(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
/** Available methods for authentication. */
|
||||
public enum AuthMethod {
|
||||
|
||||
/** App Engine internal authentication. Must always be provided as the first method. */
|
||||
INTERNAL,
|
||||
|
||||
/** Authentication methods suitable for API-style access, such as OAuth 2. */
|
||||
API,
|
||||
|
||||
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
|
||||
LEGACY
|
||||
}
|
||||
|
||||
/** User authorization policy options. */
|
||||
public enum UserPolicy {
|
||||
|
||||
/** This action ignores end users; the only configured auth method must be INTERNAL. */
|
||||
IGNORED,
|
||||
|
||||
/** No user policy is enforced; anyone can access this action. */
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* If there is a user, it must be an admin, as determined by isUserAdmin().
|
||||
*
|
||||
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
|
||||
* including editors and viewers, is an admin.
|
||||
*/
|
||||
ADMIN
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to authenticate and authorize the user, according to the settings of the action.
|
||||
*
|
||||
@@ -169,7 +119,7 @@ public class RequestAuthenticator {
|
||||
return authResult;
|
||||
}
|
||||
break;
|
||||
// API-based user authentication mechanisms, such as OAuth
|
||||
// API-based user authentication mechanisms, such as OAuth
|
||||
case API:
|
||||
// checkAuthConfig will have insured that the user policy is not IGNORED.
|
||||
for (AuthenticationMechanism authMechanism : apiAuthenticationMechanisms) {
|
||||
@@ -181,7 +131,7 @@ public class RequestAuthenticator {
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Legacy authentication via UserService
|
||||
// Legacy authentication via UserService
|
||||
case LEGACY:
|
||||
// checkAuthConfig will have insured that the user policy is not IGNORED.
|
||||
authResult = legacyAuthenticationMechanism.authenticate(req);
|
||||
@@ -209,7 +159,7 @@ public class RequestAuthenticator {
|
||||
"Actions with INTERNAL auth method may not require USER auth level");
|
||||
checkArgument(
|
||||
!(auth.userPolicy().equals(UserPolicy.IGNORED)
|
||||
&& !authMethods.equals(ImmutableList.of(AuthMethod.INTERNAL))),
|
||||
&& !authMethods.equals(ImmutableList.of(AuthMethod.INTERNAL))),
|
||||
"Actions with auth methods beyond INTERNAL must not specify the IGNORED user policy");
|
||||
}
|
||||
}
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
// 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.AuthModule.ServiceAccount;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests signed by Service Account
|
||||
*
|
||||
* <p>Currently used by cloud scheduler service account
|
||||
*/
|
||||
public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
|
||||
@Inject
|
||||
public ServiceAccountAuthenticationMechanism(
|
||||
@ServiceAccount TokenVerifier tokenVerifier,
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails) {
|
||||
super(tokenVerifier);
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
String rawToken = request.getHeader(AUTHORIZATION);
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(emailAddress))) {
|
||||
return AuthResult.create(APP);
|
||||
} else {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.tld.Registries.findTldForNameOrThrow;
|
||||
import static google.registry.model.tld.Tlds.findTldForNameOrThrow;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
|
||||
import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -28,10 +28,10 @@ import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
@@ -459,8 +459,8 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
} finally {
|
||||
// Manually reset the cache here so that subsequent commands (e.g. in SetupOteCommand) see
|
||||
// the latest version of the data.
|
||||
// TODO(b/24903801): change all those places to use uncached code paths to get Registries.
|
||||
Registries.resetCache();
|
||||
// TODO(b/24903801): change all those places to use uncached code paths to get TLDs.
|
||||
Tlds.resetCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.common.collect.Maps.filterValues;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static google.registry.model.tld.Registries.findTldForNameOrThrow;
|
||||
import static google.registry.model.tld.Tlds.findTldForNameOrThrow;
|
||||
import static google.registry.tools.CommandUtilities.addHeader;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.common.io.ByteStreams;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.rde.RdeUtil;
|
||||
import google.registry.rde.RdeUtils;
|
||||
import google.registry.rde.RydeEncoder;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -59,7 +59,7 @@ final class EscrowDepositEncryptor {
|
||||
throws IOException, XmlException {
|
||||
try (InputStream xmlFileInput = Files.newInputStream(xmlFile);
|
||||
BufferedInputStream xmlInput = new BufferedInputStream(xmlFileInput, PEEK_BUFFER_SIZE)) {
|
||||
DateTime watermark = RdeUtil.peekWatermark(xmlInput);
|
||||
DateTime watermark = RdeUtils.peekWatermark(xmlInput);
|
||||
String name = RdeNamingUtils.makeRydeFilename(tld, watermark, mode, 1, revision);
|
||||
Path rydePath = outdir.resolve(name + ".ryde");
|
||||
Path sigPath = outdir.resolve(name + ".sig");
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.rde.RdeModule.PARAM_DIRECTORY;
|
||||
import static google.registry.rde.RdeModule.PARAM_LENIENT;
|
||||
import static google.registry.rde.RdeModule.PARAM_MANUAL;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.joda.time.Duration.standardMinutes;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
@@ -23,9 +23,9 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -53,7 +53,7 @@ final class ListCursorsCommand implements Command {
|
||||
public void run() {
|
||||
Map<String, VKey<Cursor>> cursorKeys =
|
||||
cursorType.isScoped()
|
||||
? Registries.getTlds().stream()
|
||||
? Tlds.getTlds().stream()
|
||||
.map(Tld::get)
|
||||
.filter(r -> r.getTldType() == filterTldType)
|
||||
.filter(r -> !filterEscrowEnabled || r.getEscrowEnabled())
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.loadtest.LoadTestAction;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tlds;
|
||||
|
||||
/** Command to initiate a load-test. */
|
||||
@Parameters(separators = " =", commandDescription = "Run a load test.")
|
||||
@@ -91,7 +91,7 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
|
||||
}
|
||||
|
||||
// Check validity of TLD and Client Id.
|
||||
if (!Registries.getTlds().contains(tld)) {
|
||||
if (!Tlds.getTlds().contains(tld)) {
|
||||
System.err.printf("No such TLD: %s\n", tld);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ class RequestFactoryModule {
|
||||
*
|
||||
* <p>If we need to have an IAP-enabled audience, we can use the existing refresh token and the
|
||||
* IAP client ID audience to request an IAP-enabled ID token. This token is read and used by
|
||||
* {@link google.registry.request.auth.IapHeaderAuthenticationMechanism}, and it requires that the
|
||||
* user have a {@link google.registry.model.console.User} object present in the database.
|
||||
* {@link IapHeaderAuthenticationMechanismMechanism}, and it requires that the user have a {@link
|
||||
* google.registry.model.console.User} object present in the database.
|
||||
*/
|
||||
private static final GenericUrl TOKEN_SERVER_URL =
|
||||
new GenericUrl(URI.create("https://oauth2.googleapis.com/token"));
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
package google.registry.whois;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import java.net.InetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -72,8 +72,7 @@ final class NameserverLookupByIpCommand implements WhoisCommand {
|
||||
Streams.stream(hostsFromDb)
|
||||
.filter(
|
||||
host ->
|
||||
Registries.findTldForName(InternetDomainName.from(host.getHostName()))
|
||||
.isPresent())
|
||||
Tlds.findTldForName(InternetDomainName.from(host.getHostName())).isPresent())
|
||||
.collect(toImmutableList());
|
||||
if (hosts.isEmpty()) {
|
||||
throw new WhoisException(now, SC_NOT_FOUND, "No nameservers found.");
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.whois;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.math.BigDecimal;
|
||||
@@ -603,14 +603,14 @@ class RegistrarTest extends EntityTestCase {
|
||||
// Cache duration in tests is 0. To make sure the data isn't in the cache we have to set it
|
||||
// to a higher value and reset the cache.
|
||||
RegistryConfig.CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds = 600;
|
||||
Registries.resetCache();
|
||||
Tlds.resetCache();
|
||||
// Make sure the TLD we want to create doesn't exist yet.
|
||||
// This is also important because getTlds fills out the cache when used.
|
||||
assertThat(Registries.getTlds()).doesNotContain("newtld");
|
||||
assertThat(Tlds.getTlds()).doesNotContain("newtld");
|
||||
// We can't use createTld here because it fails when the cache is used.
|
||||
persistResource(newTld("newtld", "NEWTLD"));
|
||||
// Make sure we set up the cache correctly, so the newly created TLD isn't in the cache
|
||||
assertThat(Registries.getTlds()).doesNotContain("newtld");
|
||||
assertThat(Tlds.getTlds()).doesNotContain("newtld");
|
||||
|
||||
// Test that the uncached version works
|
||||
assertThat(
|
||||
@@ -633,11 +633,11 @@ class RegistrarTest extends EntityTestCase {
|
||||
|
||||
// Make sure the cache hasn't expired during the test and "newtld" is still not in the cached
|
||||
// TLDs
|
||||
assertThat(Registries.getTlds()).doesNotContain("newtld");
|
||||
assertThat(Tlds.getTlds()).doesNotContain("newtld");
|
||||
} finally {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds =
|
||||
origSingletonCacheRefreshSeconds;
|
||||
Registries.resetCache();
|
||||
Tlds.resetCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -28,8 +28,8 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link Registries}. */
|
||||
class RegistriesTest {
|
||||
/** Unit tests for {@link Tlds}. */
|
||||
class TldsTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
@@ -42,51 +42,51 @@ class RegistriesTest {
|
||||
@Test
|
||||
void testGetTlds() {
|
||||
initTestTlds();
|
||||
assertThat(Registries.getTlds()).containsExactly("foo", "a.b.c");
|
||||
assertThat(Tlds.getTlds()).containsExactly("foo", "a.b.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getTldEntities() {
|
||||
initTestTlds();
|
||||
persistResource(newTld("testtld", "TESTTLD").asBuilder().setTldType(TldType.TEST).build());
|
||||
assertThat(Registries.getTldEntitiesOfType(TldType.REAL))
|
||||
assertThat(Tlds.getTldEntitiesOfType(TldType.REAL))
|
||||
.containsExactly(Tld.get("foo"), Tld.get("a.b.c"));
|
||||
assertThat(Registries.getTldEntitiesOfType(TldType.TEST)).containsExactly(Tld.get("testtld"));
|
||||
assertThat(Tlds.getTldEntitiesOfType(TldType.TEST)).containsExactly(Tld.get("testtld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTlds_withNoRegistriesPersisted_returnsEmptySet() {
|
||||
assertThat(Registries.getTlds()).isEmpty();
|
||||
assertThat(Tlds.getTlds()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertTldExists_doesExist() {
|
||||
initTestTlds();
|
||||
Registries.assertTldExists("foo");
|
||||
Registries.assertTldExists("a.b.c");
|
||||
Tlds.assertTldExists("foo");
|
||||
Tlds.assertTldExists("a.b.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertTldExists_doesntExist() {
|
||||
initTestTlds();
|
||||
assertThrows(IllegalArgumentException.class, () -> Registries.assertTldExists("baz"));
|
||||
assertThrows(IllegalArgumentException.class, () -> Tlds.assertTldExists("baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindTldForName() {
|
||||
initTestTlds();
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("example.foo")).get().toString())
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("example.foo")).get().toString())
|
||||
.isEqualTo("foo");
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("x.y.a.b.c")).get().toString())
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("x.y.a.b.c")).get().toString())
|
||||
.isEqualTo("a.b.c");
|
||||
// We don't have an "example" tld.
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("foo.example"))).isEmpty();
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("foo.example"))).isEmpty();
|
||||
// A tld is not a match for itself.
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("foo"))).isEmpty();
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("foo"))).isEmpty();
|
||||
// The name must match the entire tld.
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("x.y.a.b"))).isEmpty();
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("x.y.b.c"))).isEmpty();
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("x.y.a.b"))).isEmpty();
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("x.y.b.c"))).isEmpty();
|
||||
// Substring tld matches aren't considered.
|
||||
assertThat(Registries.findTldForName(InternetDomainName.from("example.barfoo"))).isEmpty();
|
||||
assertThat(Tlds.findTldForName(InternetDomainName.from("example.barfoo"))).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,8 @@ import com.google.gson.JsonObject;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Actions;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
|
||||
@@ -63,6 +63,7 @@ import google.registry.xjc.rdereport.XjcRdeReportReport;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
@@ -95,7 +96,7 @@ public class RdeReportActionTest {
|
||||
new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
private final BlobId reportFile =
|
||||
BlobId.of("tub", "test_2006-06-06_full_S1_R0-report.xml.ghostryde");
|
||||
BlobId.of("tub", "job-name/test_2006-06-06_full_S1_R0-report.xml.ghostryde");
|
||||
private Tld registry;
|
||||
|
||||
private RdeReportAction createAction() {
|
||||
@@ -114,7 +115,7 @@ public class RdeReportActionTest {
|
||||
action.timeout = standardSeconds(30);
|
||||
action.stagingDecryptionKey = new FakeKeyringModule().get().getRdeStagingDecryptionKey();
|
||||
action.runner = runner;
|
||||
action.prefix = Optional.empty();
|
||||
action.prefix = Optional.of("job-name/");
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -170,11 +171,54 @@ public class RdeReportActionTest {
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
RdeReportAction action = createAction();
|
||||
action.prefix = Optional.of("job-name/");
|
||||
action.runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRunWithLock_withoutPrefix_noPrefixFound() throws Exception {
|
||||
RdeReportAction action = createAction();
|
||||
action.prefix = Optional.empty();
|
||||
assertThrows(NoContentException.class, () -> action.runWithLock(loadRdeReportCursor()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRunWithLock_withoutPrefix() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
RdeReportAction action = createAction();
|
||||
action.prefix = Optional.empty();
|
||||
gcsUtils.delete(reportFile);
|
||||
BlobId otherReportFile1 =
|
||||
BlobId.of(
|
||||
"tub", "rde-2006-06-06t00-00-00z-1/test_2006-06-06_full_S1_R0-report.xml.ghostryde");
|
||||
BlobId otherReportFile2 =
|
||||
BlobId.of(
|
||||
"tub", "rde-2006-06-06t00-00-00z-2/test_2006-06-06_full_S1_R1-report.xml.ghostryde");
|
||||
// This file's content is not correct, if it is read, the action should throw.
|
||||
gcsUtils.createFromBytes(
|
||||
BlobId.of("tub", "job-name/test_2006-06-06_full_S1_R0-report.xml.ghostryde"),
|
||||
Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
otherReportFile1,
|
||||
Ghostryde.encode(
|
||||
ByteSource.wrap("BAD DATA".getBytes(StandardCharsets.UTF_8)).read(), encryptKey));
|
||||
gcsUtils.createFromBytes(otherReportFile2, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 1));
|
||||
action.runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
@@ -198,7 +242,7 @@ public class RdeReportActionTest {
|
||||
@Test
|
||||
void testRunWithLock_regeneratedReport() throws Exception {
|
||||
gcsUtils.delete(reportFile);
|
||||
BlobId newReport = BlobId.of("tub", "test_2006-06-06_full_S1_R1-report.xml.ghostryde");
|
||||
BlobId newReport = BlobId.of("tub", "job-name/test_2006-06-06_full_S1_R1-report.xml.ghostryde");
|
||||
PGPPublicKey encryptKey = new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
gcsUtils.createFromBytes(newReport, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 1));
|
||||
|
||||
@@ -343,7 +343,7 @@ public class RdeUploadActionTest {
|
||||
gcsUtils.delete(GHOSTRYDE_FILE);
|
||||
gcsUtils.delete(LENGTH_FILE);
|
||||
gcsUtils.delete(REPORT_FILE);
|
||||
// Add a folder that is alphabetically before the desired folder and fill it will nonsense data.
|
||||
// Add a folder that is alphabetically before the desired folder and fill it with nonsense data.
|
||||
// It should NOT be picked up.
|
||||
BlobId ghostrydeFileWithPrefixBefore =
|
||||
BlobId.of("bucket", JOB_PREFIX + "-job-nama/tld_2010-10-17_full_S1_R0.xml.ghostryde");
|
||||
@@ -464,7 +464,7 @@ public class RdeUploadActionTest {
|
||||
action.sftpCooldown = standardHours(2);
|
||||
DateTime stagingCursor = DateTime.parse("2010-10-18TZ");
|
||||
DateTime uploadCursor = DateTime.parse("2010-10-17TZ");
|
||||
DateTime sftpCursor = uploadCursor.minusMinutes(97); // Within the 2 hour cooldown period.
|
||||
DateTime sftpCursor = uploadCursor.minusMinutes(97); // Within the 2-hour cooldown period.
|
||||
persistResource(Cursor.createScoped(RDE_STAGING, stagingCursor, Tld.get("tld")));
|
||||
persistResource(Cursor.createScoped(RDE_UPLOAD_SFTP, sftpCursor, Tld.get("tld")));
|
||||
NoContentException thrown =
|
||||
@@ -477,7 +477,7 @@ public class RdeUploadActionTest {
|
||||
+ " ago)");
|
||||
}
|
||||
|
||||
private String slurp(InputStream is) throws IOException {
|
||||
private static String slurp(InputStream is) throws IOException {
|
||||
return CharStreams.toString(new InputStreamReader(is, UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ import static org.mockito.Mockito.when;
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.common.testing.NullPointerTester;
|
||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.RequestAuthenticator;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.util.JdkLoggerConfig;
|
||||
import java.util.Optional;
|
||||
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.truth.Truth8;
|
||||
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 javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
/** Tests for {@link IapHeaderAuthenticationMechanism}. */
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class IapHeaderAuthenticationMechanismTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaTestExtensions.JpaUnitTestExtension jpaExtension =
|
||||
new JpaTestExtensions.Builder().withEntityClass(User.class).buildUnitTestExtension();
|
||||
|
||||
@Mock private TokenVerifier tokenVerifier;
|
||||
@Mock private HttpServletRequest request;
|
||||
|
||||
private JsonWebSignature token;
|
||||
private IapHeaderAuthenticationMechanism authenticationMechanism;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
authenticationMechanism = new IapHeaderAuthenticationMechanism(tokenVerifier);
|
||||
when(request.getHeader("X-Goog-IAP-JWT-Assertion")).thenReturn("jwtValue");
|
||||
Payload payload = new Payload();
|
||||
payload.setEmail("email@email.com");
|
||||
payload.setSubject("gaiaId");
|
||||
token = new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
|
||||
when(tokenVerifier.verify("jwtValue")).thenReturn(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_validUser() throws Exception {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
insertInDb(user);
|
||||
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("idToken", "asdf")});
|
||||
when(tokenVerifier.verify("asdf")).thenReturn(token);
|
||||
AuthResult authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
Truth8.assertThat(authResult.userAuthInfo()).isPresent();
|
||||
Truth8.assertThat(authResult.userAuthInfo().get().consoleUser()).hasValue(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_noCookie() {
|
||||
when(request.getCookies()).thenReturn(new Cookie[0]);
|
||||
assertThat(authenticationMechanism.authenticate(request).isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badToken() throws Exception {
|
||||
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("idToken", "asdf")});
|
||||
when(tokenVerifier.verify("asdf")).thenReturn(null);
|
||||
assertThat(authenticationMechanism.authenticate(request).isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_errorVerifyingToken() throws Exception {
|
||||
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("idToken", "asdf")});
|
||||
when(tokenVerifier.verify("asdf")).thenThrow(new TokenVerifier.VerificationException("hi"));
|
||||
assertThat(authenticationMechanism.authenticate(request).isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_goodTokenButUnknownUser() throws Exception {
|
||||
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("idToken", "asdf")});
|
||||
when(tokenVerifier.verify("asdf")).thenReturn(token);
|
||||
assertThat(authenticationMechanism.authenticate(request).isAuthenticated()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.google.appengine.api.users.User;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.FakeClock;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
// 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
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.request.auth.AuthModule.PROXY_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;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.auth.oauth2.TokenVerifier.VerificationException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
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.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.IapOidcAuthenticationMechanism;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.RegularOidcAuthenticationMechanism;
|
||||
import javax.inject.Singleton;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link OidcTokenAuthenticationMechanism}. */
|
||||
public class OidcTokenAuthenticationMechanismTest {
|
||||
|
||||
private static final String rawToken = "this-token";
|
||||
private static final String email = "user@email.test";
|
||||
private static final String gaiaId = "gaia-id";
|
||||
private static final ImmutableList<String> serviceAccounts =
|
||||
ImmutableList.of("service@email.test", "email@service.goog");
|
||||
|
||||
private final Payload payload = new Payload();
|
||||
private final User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(email)
|
||||
.setGaiaId(gaiaId)
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
private final JsonWebSignature jwt =
|
||||
new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
|
||||
private final TokenVerifier tokenVerifier = mock(TokenVerifier.class);
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
private AuthResult authResult;
|
||||
private OidcTokenAuthenticationMechanism authenticationMechanism =
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, e -> rawToken) {};
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaTestExtensions.JpaUnitTestExtension jpaExtension =
|
||||
new JpaTestExtensions.Builder().withEntityClass(User.class).buildUnitTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
when(tokenVerifier.verify(eq(rawToken))).thenReturn(jwt);
|
||||
payload.setEmail(email);
|
||||
payload.setSubject(gaiaId);
|
||||
insertInDb(user);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
OidcTokenAuthenticationMechanism.unsetAuthResultForTesting();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthResultBypass() {
|
||||
OidcTokenAuthenticationMechanism.setAuthResultForTesting(AuthResult.create(AuthLevel.APP));
|
||||
assertThat(authenticationMechanism.authenticate(null))
|
||||
.isEqualTo(AuthResult.create(AuthLevel.APP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_noTokenFromRequest() {
|
||||
authenticationMechanism =
|
||||
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, 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"));
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_noEmailAddress() throws Exception {
|
||||
payload.setEmail(null);
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_user() throws Exception {
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.userAuthInfo().get().consoleUser().get()).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_serviceAccount() throws Exception {
|
||||
payload.setEmail("service@email.test");
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_bothUserAndServiceAccount() throws Exception {
|
||||
User serviceUser =
|
||||
new User.Builder()
|
||||
.setEmailAddress("service@email.test")
|
||||
.setGaiaId("service-gaia-id")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
insertInDb(serviceUser);
|
||||
payload.setEmail("service@email.test");
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.userAuthInfo().get().consoleUser().get()).isEqualTo(serviceUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticate_unknownEmailAddress() throws Exception {
|
||||
payload.setEmail("bad-guy@evil.real");
|
||||
authResult = authenticationMechanism.authenticate(request);
|
||||
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIap_tokenExtractor() throws Exception {
|
||||
useIapOidcMechanism();
|
||||
when(request.getHeader(IAP_HEADER_NAME)).thenReturn(rawToken);
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegular_tokenExtractor() throws Exception {
|
||||
useRegularOidcMechanism();
|
||||
// The token does not have the "Bearer " prefix.
|
||||
when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(rawToken);
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isNull();
|
||||
|
||||
// The token is in the correct format.
|
||||
when(request.getHeader(PROXY_HEADER_NAME))
|
||||
.thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken));
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken);
|
||||
|
||||
// The token is in the correct format, and under the alternative header.
|
||||
when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(null);
|
||||
when(request.getHeader(AUTHORIZATION))
|
||||
.thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken));
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken);
|
||||
}
|
||||
|
||||
private void useIapOidcMechanism() {
|
||||
TestComponent component = DaggerOidcTokenAuthenticationMechanismTest_TestComponent.create();
|
||||
authenticationMechanism = component.iapOidcAuthenticationMechanism();
|
||||
}
|
||||
|
||||
private void useRegularOidcMechanism() {
|
||||
TestComponent component = DaggerOidcTokenAuthenticationMechanismTest_TestComponent.create();
|
||||
authenticationMechanism = component.regularOidcAuthenticationMechanism();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {AuthModule.class, TestModule.class})
|
||||
interface TestComponent {
|
||||
IapOidcAuthenticationMechanism iapOidcAuthenticationMechanism();
|
||||
|
||||
RegularOidcAuthenticationMechanism regularOidcAuthenticationMechanism();
|
||||
}
|
||||
|
||||
@Module
|
||||
static class TestModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("projectIdNumber")
|
||||
long provideProjectIdNumber() {
|
||||
return 12345;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("projectId")
|
||||
String provideProjectId() {
|
||||
return "my-project";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("serviceAccountEmails")
|
||||
ImmutableList<String> provideServiceAccountEmails() {
|
||||
return serviceAccounts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthMethod;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthSettings;
|
||||
import google.registry.request.auth.RequestAuthenticator.UserPolicy;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeOAuthService;
|
||||
@@ -52,50 +52,42 @@ class RequestAuthenticatorTest {
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL), AuthLevel.NONE, UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_INTERNAL_OR_ADMIN = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_INTERNAL_OR_ADMIN =
|
||||
AuthSettings.create(ImmutableList.of(AuthMethod.INTERNAL), AuthLevel.APP, UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_ANY_USER_ANY_METHOD = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.PUBLIC);
|
||||
private static final AuthSettings AUTH_ANY_USER_ANY_METHOD =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_ANY_USER_NO_LEGACY = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.PUBLIC);
|
||||
private static final AuthSettings AUTH_ANY_USER_NO_LEGACY =
|
||||
AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.USER, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_ADMIN_USER_ANY_METHOD = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.ADMIN);
|
||||
private static final AuthSettings AUTH_ADMIN_USER_ANY_METHOD =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.ADMIN);
|
||||
|
||||
private static final AuthSettings AUTH_NO_METHODS = AuthSettings.create(
|
||||
ImmutableList.of(),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_NO_METHODS =
|
||||
AuthSettings.create(ImmutableList.of(), AuthLevel.APP, UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_WRONG_METHOD_ORDERING = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.INTERNAL),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_WRONG_METHOD_ORDERING =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.INTERNAL), AuthLevel.APP, UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_DUPLICATE_METHODS = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.API),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_DUPLICATE_METHODS =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.API),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_INTERNAL_WITH_USER = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_INTERNAL_WITH_USER =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.IGNORED);
|
||||
|
||||
private static final AuthSettings AUTH_WRONGLY_IGNORING_USER = AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
|
||||
AuthLevel.APP,
|
||||
UserPolicy.IGNORED);
|
||||
private static final AuthSettings AUTH_WRONGLY_IGNORING_USER =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API), AuthLevel.APP, UserPolicy.IGNORED);
|
||||
|
||||
private final UserService mockUserService = mock(UserService.class);
|
||||
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
// 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class ServiceAccountAuthenticationMechanismTest {
|
||||
|
||||
@Mock private TokenVerifier tokenVerifier;
|
||||
@Mock private HttpServletRequest request;
|
||||
|
||||
private JsonWebSignature token;
|
||||
private ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
serviceAccountAuthenticationMechanism =
|
||||
new ServiceAccountAuthenticationMechanism(
|
||||
tokenVerifier, ImmutableList.of("sa-prefix@email.com", "cloud-tasks@email.com"));
|
||||
when(request.getHeader(AUTHORIZATION)).thenReturn("Bearer jwtValue");
|
||||
Payload payload = new Payload();
|
||||
payload.setEmail("sa-prefix@email.com");
|
||||
token = new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
|
||||
when(tokenVerifier.verify("jwtValue")).thenReturn(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_authenticates() throws Exception {
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_secondEmail() throws Exception {
|
||||
Payload payload = new Payload();
|
||||
payload.setEmail("cloud-tasks@email.com");
|
||||
token = new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
|
||||
when(tokenVerifier.verify("jwtValue")).thenReturn(token);
|
||||
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_authenticateWrongEmail() throws Exception {
|
||||
token.getPayload().set("email", "not-service-account-email@email.com");
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_authenticateWrongHeader() throws Exception {
|
||||
when(request.getHeader(AUTHORIZATION)).thenReturn("BEARER asd");
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,10 @@ import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.request.auth.IapHeaderAuthenticationMechanism;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.UserInfo;
|
||||
import google.registry.testing.UserServiceExtension;
|
||||
import google.registry.tools.params.HostAndPortParameter;
|
||||
@@ -145,7 +148,8 @@ public final class RegistryTestServerMain {
|
||||
.setUserRoles(userRoles)
|
||||
.setRegistryLockPassword("registryLockPassword")
|
||||
.build();
|
||||
IapHeaderAuthenticationMechanism.setUserAuthInfoForTestServer(user);
|
||||
OidcTokenAuthenticationMechanism.setAuthResultForTesting(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user)));
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension().beforeEach(null);
|
||||
JpaTransactionManagerExtension.loadInitialData();
|
||||
System.out.printf("%sLoading fixtures...%s\n", BLUE, RESET);
|
||||
|
||||
+1
-1
@@ -25,8 +25,8 @@ import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
|
||||
+1
-1
@@ -35,8 +35,8 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
|
||||
+1
-1
@@ -35,8 +35,8 @@ import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.google.common.net.MediaType;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
|
||||
+1
-1
@@ -42,8 +42,8 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.ResponseImpl;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
|
||||
+1
-1
@@ -39,8 +39,8 @@ import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeClock;
|
||||
|
||||
+1
-1
@@ -43,8 +43,8 @@ import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.ResponseImpl;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
|
||||
+1
-1
@@ -42,8 +42,8 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.registrar.Registrar.State.ACTIVE;
|
||||
import static google.registry.model.registrar.Registrar.Type.PDT;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -141,3 +141,4 @@ V140__rename_process_time_column_in_dns_refresh_request_table.sql
|
||||
V141__add_ttl_columns_to_tld.sql
|
||||
V142__drop_request_log_id.sql
|
||||
V143__idn_per_tld.sql
|
||||
V144__drop_database_migration_state_schedule_table.sql
|
||||
|
||||
+15
@@ -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.
|
||||
|
||||
DROP TABLE "DatabaseMigrationStateSchedule";
|
||||
@@ -318,16 +318,6 @@ CREATE TABLE public."Cursor" (
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DatabaseMigrationStateSchedule; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public."DatabaseMigrationStateSchedule" (
|
||||
id bigint NOT NULL,
|
||||
migration_transitions public.hstore
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DelegationSignerData; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1301,14 +1291,6 @@ ALTER TABLE ONLY public."Cursor"
|
||||
ADD CONSTRAINT "Cursor_pkey" PRIMARY KEY (scope, type);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DatabaseMigrationStateSchedule DatabaseMigrationStateSchedule_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."DatabaseMigrationStateSchedule"
|
||||
ADD CONSTRAINT "DatabaseMigrationStateSchedule_pkey" PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: DelegationSignerData DelegationSignerData_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1572,13 +1554,6 @@ ALTER TABLE ONLY public."User"
|
||||
CREATE INDEX allocation_token_domain_name_idx ON public."AllocationToken" USING btree (domain_name);
|
||||
|
||||
|
||||
--
|
||||
-- Name: database_migration_state_schedule_singleton; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX database_migration_state_schedule_singleton ON public."DatabaseMigrationStateSchedule" USING btree ((true));
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_history_to_ds_data_history_idx; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Registry Console Swagger API
|
||||
|
||||
This is the Swagger-based Registry Console API documentation. The project endpoints documentation in json format can be found in `console-api-swagger.json`, rest of the files in the folder compose Swagger standalone distributive stripped to bare miminum necessary to start the Swagger UI.
|
||||
|
||||
## How to run Swagger to view the endpoints documentation
|
||||
|
||||
Since Swagger standalone distributive is nothing but a simple static website it's extremely easy to get it started and view the documentation in a matter of seconds.
|
||||
Note - just a simple opening of `index.html` wouldn't be enough, as with any static website, it'd not be able to handle relative resources requests therefore succesfully render Swagger UI. For this reason it's configured to spin off a simple node.js based http server to serve Swagger UI resources. The following steps required to succesfully start the Swagger UI:
|
||||
|
||||
* Install npm dependencies - `npm install`
|
||||
* Run - `npm run swagger`
|
||||
* Make changes in `console-api-swagger.json`
|
||||
* Upon making changes make sure your browser cache is turned off
|
||||
|
||||
|
||||
## How to update the Swagger UI
|
||||
|
||||
In order to update Swagger version the following steps should to be taken:
|
||||
|
||||
* [Download](https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/) Swagger standalone distributive
|
||||
* Remove `.map.*` files as they are only needed to debug Swagger UI and not to run it
|
||||
* Add the link to Console API Swagger documentation file - `console-api-swagger.json` to the `swagger-initializer.js`
|
||||
* Copy with replace into the current directory
|
||||
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Registry Console API",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/console-api/user": {
|
||||
"get": {
|
||||
"summary": "Provides user type to UI",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum":[
|
||||
"ADMIN",
|
||||
"USER"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/console-api/poll": {
|
||||
"post": {
|
||||
"summary": "Every T minutes (1?) request/response to keep front-end in sync with the back-end",
|
||||
"parameters": [{
|
||||
"name": "version",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}, {
|
||||
"name": "registrarId",
|
||||
"in": "body",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}, {
|
||||
"name": "notificationId",
|
||||
"in": "body",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}],
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"notificationText": {
|
||||
"type": "string"
|
||||
},
|
||||
"notificationId": {
|
||||
"type": "string"
|
||||
},
|
||||
"dismissable": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/console-api/registrars": {
|
||||
"get": {
|
||||
"summary": "Provides list of available registrars to UI",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"registrarId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/console-api/domains": {
|
||||
"get": {
|
||||
"summary": "GET list of domains per registrar with pagination and filters",
|
||||
"parameters": [{
|
||||
"name": "registrarId",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}, {
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "number",
|
||||
"default": 10
|
||||
}, {
|
||||
"name": "marker",
|
||||
"description": "Name of the last domain retrieved by the user",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}, {
|
||||
"name": "filterBy",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"content": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tld": {
|
||||
"type":"array",
|
||||
"items": {
|
||||
"type":"string"
|
||||
}
|
||||
},
|
||||
"status":{
|
||||
"type":"array",
|
||||
"items": {
|
||||
"type":"string"
|
||||
}
|
||||
},
|
||||
"isLocked": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Provides list of domains per registrar",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tld": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum":[
|
||||
"CLIENT_DELETE_PROHIBITED",
|
||||
"CLIENT_HOLD",
|
||||
"CLIENT_RENEW_PROHIBITED",
|
||||
"CLIENT_TRANSFER_PROHIBITED",
|
||||
"CLIENT_UPDATE_PROHIBITED",
|
||||
"INACTIVE",
|
||||
"LINKED",
|
||||
"OK",
|
||||
"PENDING_CREATE",
|
||||
"PENDING_DELETE",
|
||||
"PENDING_TRANSFER",
|
||||
"PENDING_UPDATE",
|
||||
"SERVER_DELETE_PROHIBITED",
|
||||
"SERVER_HOLD",
|
||||
"SERVER_RENEW_PROHIBITED",
|
||||
"SERVER_TRANSFER_PROHIBITED",
|
||||
"SERVER_UPDATE_PROHIBITED"
|
||||
]
|
||||
}
|
||||
},
|
||||
"isLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"creationTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"expirationTime": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/console-api/domain": {
|
||||
"post": {
|
||||
"summary": "Apply action on a domain",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Action has been succesfully applied"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "domain",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
},
|
||||
{
|
||||
"name": "registrarId",
|
||||
"in": "body",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
},
|
||||
{
|
||||
"name": "action",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/console-api/settings/contacts/get": {
|
||||
"get": {
|
||||
"summary": "GET settings contact page",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Provides list of contacts per registrar",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/settingsContacts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "registrarId",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/console-api/settings/contacts/upd": {
|
||||
"post": {
|
||||
"summary": "Creates, updates or delete registrar contacts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"schema": {
|
||||
"$ref": "#/definitions/settingsContacts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "registrarId",
|
||||
"in": "body",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
},
|
||||
{
|
||||
"name": "contacts",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"type": "array",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/settingsContacts"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"definitions": {
|
||||
"settingsContacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"registrarId": {
|
||||
"type": "string"
|
||||
},
|
||||
"registryLockEmailAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"faxNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"types": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ABUSE",
|
||||
"ADMIN",
|
||||
"BILLING",
|
||||
"LEGAL",
|
||||
"MARKETING",
|
||||
"TECH",
|
||||
"WHOIS"
|
||||
]
|
||||
},
|
||||
"visibleInWhoisAsAdmin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"visibleInWhoisAsTech": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"visibleInDomainWhoisAsAbuse": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allowedToSetRegistryLockPassword": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,16 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+780
@@ -0,0 +1,780 @@
|
||||
{
|
||||
"name": "swagger",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "swagger",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"http-server": "^14.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/corser": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
|
||||
"integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-encoding": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
||||
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eventemitter3": "^4.0.0",
|
||||
"follow-redirects": "^1.0.0",
|
||||
"requires-port": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
|
||||
"integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"basic-auth": "^2.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"corser": "^2.0.1",
|
||||
"he": "^1.2.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"mime": "^1.6.0",
|
||||
"minimist": "^1.2.6",
|
||||
"opener": "^1.5.1",
|
||||
"portfinder": "^1.0.28",
|
||||
"secure-compare": "3.0.1",
|
||||
"union": "~0.5.0",
|
||||
"url-join": "^4.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"http-server": "bin/http-server"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/portfinder": {
|
||||
"version": "1.0.32",
|
||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
|
||||
"integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async": "^2.6.4",
|
||||
"debug": "^3.2.7",
|
||||
"mkdirp": "^0.5.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/secure-compare": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
|
||||
"integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/union": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
|
||||
"integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"qs": "^6.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/whatwg-encoding": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"async": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"corser": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
|
||||
"integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"dev": true
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"dev": true
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true
|
||||
},
|
||||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true
|
||||
},
|
||||
"html-encoding-sniffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"whatwg-encoding": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"http-proxy": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
||||
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eventemitter3": "^4.0.0",
|
||||
"follow-redirects": "^1.0.0",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"http-server": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
|
||||
"integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"basic-auth": "^2.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"corser": "^2.0.1",
|
||||
"he": "^1.2.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"mime": "^1.6.0",
|
||||
"minimist": "^1.2.6",
|
||||
"opener": "^1.5.1",
|
||||
"portfinder": "^1.0.28",
|
||||
"secure-compare": "3.0.1",
|
||||
"union": "~0.5.0",
|
||||
"url-join": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true
|
||||
},
|
||||
"opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true
|
||||
},
|
||||
"portfinder": {
|
||||
"version": "1.0.32",
|
||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
|
||||
"integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "^2.6.4",
|
||||
"debug": "^3.2.7",
|
||||
"mkdirp": "^0.5.6"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"secure-compare": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
|
||||
"integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
|
||||
"dev": true
|
||||
},
|
||||
"side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"union": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
|
||||
"integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"qs": "^6.4.0"
|
||||
}
|
||||
},
|
||||
"url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-encoding": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"iconv-lite": "0.6.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user