mirror of
https://github.com/google/nomulus.git
synced 2025-07-30 22:46:26 +02:00
Refactor OIDC-based auth mechanism (#2049)
This PR changes the two flavors of OIDC authentication mechanisms to verify the same audience. This allows the same token to pass both mechanisms. Previously the regular OIDC flavor uses the project id as its required audience, which does not work for local user credentials (such as ones used by the nomulus tool), which requires a valid OAuth client ID as audience when minting the token (project id is NOT a valid OAuth client ID). I considered allowing multiple audiences, but the result is not as clean as just using the same everywhere, because the fall-through logic would have generated a lot of noises for failed attempts. This PR also changes the client side to solely use OIDC token whenever possible, including the proxy, cloud scheduler and cloud tasks. The nomulus tool still uses OAuth access token by default because it requires USER level authentication, which in turn requires us to fill the User table with objects corresponding to the email address of everyone needing access to the tool. TESTED=verified each client is able to make authenticated calls on QA with or without IAP.
This commit is contained in:
parent
cf1a148208
commit
fdfbb9572d
56 changed files with 565 additions and 891 deletions
|
@ -20,8 +20,6 @@ import static google.registry.tools.ServiceConnection.getServer;
|
|||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.api.gax.rpc.ApiException;
|
||||
import com.google.cloud.tasks.v2.AppEngineHttpRequest;
|
||||
import com.google.cloud.tasks.v2.AppEngineRouting;
|
||||
import com.google.cloud.tasks.v2.CloudTasksClient;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.HttpRequest;
|
||||
|
@ -39,10 +37,12 @@ import com.google.common.net.MediaType;
|
|||
import com.google.common.net.UrlEscapers;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -52,7 +52,6 @@ import java.util.Random;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
|
@ -67,9 +66,8 @@ public class CloudTasksUtils implements Serializable {
|
|||
private final Clock clock;
|
||||
private final String projectId;
|
||||
private final String locationId;
|
||||
// defaultServiceAccount and iapClientId are nullable because Optional isn't serializable
|
||||
@Nullable private final String defaultServiceAccount;
|
||||
@Nullable private final String iapClientId;
|
||||
private final String oauthClientId;
|
||||
private final GoogleCredentialsBundle credential;
|
||||
private final SerializableCloudTasksClient client;
|
||||
|
||||
@Inject
|
||||
|
@ -78,15 +76,15 @@ public class CloudTasksUtils implements Serializable {
|
|||
Clock clock,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
@Config("defaultServiceAccount") Optional<String> defaultServiceAccount,
|
||||
@Config("iapClientId") Optional<String> iapClientId,
|
||||
@Config("oauthClientId") String oauthClientId,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credential,
|
||||
SerializableCloudTasksClient client) {
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
this.projectId = projectId;
|
||||
this.locationId = locationId;
|
||||
this.defaultServiceAccount = defaultServiceAccount.orElse(null);
|
||||
this.iapClientId = iapClientId.orElse(null);
|
||||
this.oauthClientId = oauthClientId;
|
||||
this.credential = credential;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
|
@ -124,7 +122,7 @@ public class CloudTasksUtils implements Serializable {
|
|||
*
|
||||
* @return the resulting path (unchanged for POST requests, with params added for GET requests)
|
||||
*/
|
||||
private String processRequestParameters(
|
||||
private static String processRequestParameters(
|
||||
String path,
|
||||
HttpMethod method,
|
||||
Multimap<String, String> params,
|
||||
|
@ -152,43 +150,20 @@ public class CloudTasksUtils implements Serializable {
|
|||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Task} that does not use AppEngine for submission.
|
||||
*
|
||||
* <p>This uses the standard Cloud Tasks auth format to create and send an OIDC ID token set to
|
||||
* the default service account. That account must have permission to submit tasks to Cloud Tasks.
|
||||
*/
|
||||
private Task createNonAppEngineTask(
|
||||
String path, HttpMethod method, Service service, Multimap<String, String> params) {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder().setHttpMethod(method);
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
OidcToken.Builder oidcTokenBuilder =
|
||||
OidcToken.newBuilder().setServiceAccountEmail(defaultServiceAccount);
|
||||
// If the service is using IAP, add that as the audience for the token so the request can be
|
||||
// appropriately authed. Otherwise, use the project name.
|
||||
if (iapClientId != null) {
|
||||
oidcTokenBuilder.setAudience(iapClientId);
|
||||
} else {
|
||||
oidcTokenBuilder.setAudience(projectId);
|
||||
}
|
||||
requestBuilder.setOidcToken(oidcTokenBuilder.build());
|
||||
String totalPath = String.format("%s%s", getServer(service), path);
|
||||
requestBuilder.setUrl(totalPath);
|
||||
return Task.newBuilder().setHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Task} to be enqueued.
|
||||
*
|
||||
* <p>This uses the standard Cloud Tasks auth format to create and send an OIDC ID token with the
|
||||
* default service account as the principal. That account must have permission to submit tasks to
|
||||
* Cloud Tasks.
|
||||
*
|
||||
* @param path the relative URI (staring with a slash and ending without one).
|
||||
* @param method the HTTP method to be used for the request, only GET and POST are supported.
|
||||
* @param service the App Engine service to route the request to. Note that with App Engine Task
|
||||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @return the enqueued task.
|
||||
* @see <a
|
||||
|
@ -204,21 +179,18 @@ public class CloudTasksUtils implements Serializable {
|
|||
method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST),
|
||||
"HTTP method %s is used. Only GET and POST are allowed.",
|
||||
method);
|
||||
// If the default service account is configured, send a standard non-AppEngine HTTP request
|
||||
if (defaultServiceAccount != null) {
|
||||
return createNonAppEngineTask(path, method, service, params);
|
||||
} else {
|
||||
AppEngineHttpRequest.Builder requestBuilder =
|
||||
AppEngineHttpRequest.newBuilder()
|
||||
.setHttpMethod(method)
|
||||
.setAppEngineRouting(
|
||||
AppEngineRouting.newBuilder().setService(service.toString()).build());
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
requestBuilder.setRelativeUri(path);
|
||||
return Task.newBuilder().setAppEngineHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder().setHttpMethod(method);
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
OidcToken.Builder oidcTokenBuilder =
|
||||
OidcToken.newBuilder()
|
||||
.setServiceAccountEmail(credential.serviceAccount())
|
||||
.setAudience(oauthClientId);
|
||||
requestBuilder.setOidcToken(oidcTokenBuilder.build());
|
||||
String totalPath = String.format("%s%s", getServer(service), path);
|
||||
requestBuilder.setUrl(totalPath);
|
||||
return Task.newBuilder().setHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,7 +202,7 @@ public class CloudTasksUtils implements Serializable {
|
|||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @param jitterSeconds the number of seconds that a task is randomly delayed up to.
|
||||
* @return the enqueued task.
|
||||
|
@ -264,7 +236,7 @@ public class CloudTasksUtils implements Serializable {
|
|||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @param delay the amount of time that a task needs to delayed for.
|
||||
* @return the enqueued task.
|
||||
|
@ -330,6 +302,9 @@ public class CloudTasksUtils implements Serializable {
|
|||
}
|
||||
|
||||
public abstract static class SerializableCloudTasksClient implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7872861868968535498L;
|
||||
|
||||
public abstract Task enqueue(String projectId, String locationId, String queueName, Task task);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ public final class RegistryConfig {
|
|||
@Module
|
||||
public static final class ConfigModule {
|
||||
|
||||
private ConfigModule() {}
|
||||
|
||||
@Provides
|
||||
@Config("projectId")
|
||||
public static String provideProjectId(RegistryConfigSettings config) {
|
||||
|
@ -120,17 +122,6 @@ public final class RegistryConfig {
|
|||
return config.gcpProject.locationId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("serviceAccountEmails")
|
||||
public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
|
||||
return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("defaultServiceAccount")
|
||||
public static Optional<String> provideDefaultServiceAccount(RegistryConfigSettings config) {
|
||||
return Optional.ofNullable(config.gcpProject.defaultServiceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* The filename of the logo to be displayed in the header of the registrar console.
|
||||
|
@ -257,7 +248,7 @@ public final class RegistryConfig {
|
|||
@Provides
|
||||
@Config("databaseRetention")
|
||||
public static Duration provideDatabaseRetention() {
|
||||
return RegistryConfig.getDatabaseRetention();
|
||||
return getDatabaseRetention();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -304,7 +295,7 @@ public final class RegistryConfig {
|
|||
* The maximum number of domain and host updates to batch together to send to
|
||||
* PublishDnsUpdatesAction, to avoid exceeding HTTP request timeout limits.
|
||||
*
|
||||
* @see google.registry.dns.ReadDnsRefreshRequestsAction
|
||||
* @see ReadDnsRefreshRequestsAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsTldUpdateBatchSize")
|
||||
|
@ -1144,7 +1135,7 @@ public final class RegistryConfig {
|
|||
@Provides
|
||||
@Config("availableOauthScopes")
|
||||
public static ImmutableSet<String> provideAvailableOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.availableOauthScopes);
|
||||
return ImmutableSet.copyOf(config.auth.availableOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1157,27 +1148,38 @@ public final class RegistryConfig {
|
|||
* API, which requires at least one of:
|
||||
*
|
||||
* <ul>
|
||||
* <li>https://www.googleapis.com/auth/appengine.apis
|
||||
* <li>https://www.googleapis.com/auth/cloud-platform
|
||||
* <li>{@code https://www.googleapis.com/auth/appengine.apis}
|
||||
* <li>{@code https://www.googleapis.com/auth/cloud-platform}
|
||||
* </ul>
|
||||
*/
|
||||
@Provides
|
||||
@Config("requiredOauthScopes")
|
||||
public static ImmutableSet<String> provideRequiredOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.requiredOauthScopes);
|
||||
return ImmutableSet.copyOf(config.auth.requiredOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides service account email addresses allowed to authenticate with the app at {@link
|
||||
* google.registry.request.auth.AuthSettings.AuthLevel#APP} level.
|
||||
*/
|
||||
@Provides
|
||||
@Config("allowedServiceAccountEmails")
|
||||
public static ImmutableSet<String> provideAllowedServiceAccountEmails(
|
||||
RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.auth.allowedServiceAccountEmails);
|
||||
}
|
||||
|
||||
/** Provides the allowed OAuth client IDs (could be multibinding). */
|
||||
@Provides
|
||||
@Config("allowedOauthClientIds")
|
||||
public static ImmutableSet<String> provideAllowedOauthClientIds(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.allowedOauthClientIds);
|
||||
return ImmutableSet.copyOf(config.auth.allowedOauthClientIds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("iapClientId")
|
||||
public static Optional<String> provideIapClientId(RegistryConfigSettings config) {
|
||||
return Optional.ofNullable(config.oAuth.iapClientId);
|
||||
@Config("oauthClientId")
|
||||
public static String provideOauthClientId(RegistryConfigSettings config) {
|
||||
return config.auth.oauthClientId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1253,7 +1255,7 @@ public final class RegistryConfig {
|
|||
toImmutableSortedMap(
|
||||
naturalOrder(),
|
||||
e ->
|
||||
e.getKey().equals("START_OF_TIME")
|
||||
"START_OF_TIME".equals(e.getKey())
|
||||
? START_OF_TIME
|
||||
: DateTime.parse(e.getKey()),
|
||||
Entry::getValue));
|
||||
|
@ -1374,6 +1376,12 @@ public final class RegistryConfig {
|
|||
public static String providePackageDomainLimitUpgradeEmailBody(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
|
||||
private static String formatComments(String text) {
|
||||
return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream()
|
||||
.map(s -> "# " + s)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the App Engine project ID, which is based off the environment name. */
|
||||
|
@ -1539,9 +1547,9 @@ public final class RegistryConfig {
|
|||
* one single INSERT statement which can dramatically increase speed in situations with many
|
||||
* inserts.
|
||||
*
|
||||
* <p>Hibernate docs, i.e.
|
||||
* https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html,
|
||||
* recommend between 10 and 50.
|
||||
* <p><a
|
||||
* href="https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html">Hibernate
|
||||
* User Guide</a> recommends between 10 and 50.
|
||||
*/
|
||||
public static int getHibernateJdbcBatchSize() {
|
||||
return CONFIG_SETTINGS.get().hibernate.jdbcBatchSize;
|
||||
|
@ -1578,11 +1586,7 @@ public final class RegistryConfig {
|
|||
public static final Supplier<RegistryConfigSettings> CONFIG_SETTINGS =
|
||||
memoize(RegistryConfig::getConfigSettings);
|
||||
|
||||
private static String formatComments(String text) {
|
||||
return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream()
|
||||
.map(s -> "# " + s)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
|
||||
private static InternetAddress parseEmailAddress(String email) {
|
||||
try {
|
||||
|
|
|
@ -23,7 +23,7 @@ public class RegistryConfigSettings {
|
|||
|
||||
public GcpProject gcpProject;
|
||||
public GSuite gSuite;
|
||||
public OAuth oAuth;
|
||||
public Auth auth;
|
||||
public CredentialOAuth credentialOAuth;
|
||||
public RegistryPolicy registryPolicy;
|
||||
public Hibernate hibernate;
|
||||
|
@ -54,16 +54,15 @@ public class RegistryConfigSettings {
|
|||
public String backendServiceUrl;
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
public List<String> serviceAccountEmails;
|
||||
public String defaultServiceAccount;
|
||||
}
|
||||
|
||||
/** Configuration options for OAuth settings for authenticating users. */
|
||||
public static class OAuth {
|
||||
/** Configuration options for authenticating users. */
|
||||
public static class Auth {
|
||||
public List<String> availableOauthScopes;
|
||||
public List<String> requiredOauthScopes;
|
||||
public List<String> allowedOauthClientIds;
|
||||
public String iapClientId;
|
||||
public List<String> allowedServiceAccountEmails;
|
||||
public String oauthClientId;
|
||||
}
|
||||
|
||||
/** Configuration options for accessing Google APIs. */
|
||||
|
|
|
@ -18,18 +18,10 @@ gcpProject:
|
|||
# whether to use local/test credentials when connecting to the servers
|
||||
isLocal: true
|
||||
# URLs of the services for the project.
|
||||
defaultServiceUrl: https://localhost
|
||||
backendServiceUrl: https://localhost
|
||||
toolsServiceUrl: https://localhost
|
||||
pubapiServiceUrl: https://localhost
|
||||
# Service accounts eligible for authorization (e.g. default service account,
|
||||
# account used by Cloud Scheduler) to send authenticated requests.
|
||||
serviceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
# The default service account with which the service is running. For example,
|
||||
# on GAE this would be {project-id}@appspot.gserviceaccount.com
|
||||
defaultServiceAccount: null
|
||||
defaultServiceUrl: https://default.example.com
|
||||
backendServiceUrl: https://backend.example.com
|
||||
toolsServiceUrl: https://tools.example.com
|
||||
pubapiServiceUrl: https://pubapi.example.com
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
|
@ -295,24 +287,41 @@ caching:
|
|||
# long duration is acceptable because claims lists don't change frequently.
|
||||
claimsListCachingSeconds: 21600 # six hours
|
||||
|
||||
oAuth:
|
||||
# Note: Only allowedServiceAccountEmails and oauthClientId should be configured.
|
||||
# Other fields are related to OAuth-based authentication and will be removed.
|
||||
auth:
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes to detect on access tokens. Superset of requiredOauthScopes.
|
||||
availableOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes required for authenticating. Subset of availableOauthScopes.
|
||||
requiredOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth client IDs that are allowed to authenticate and communicate with
|
||||
# backend services, e. g. nomulus tool, EPP proxy, etc. The client_id value
|
||||
# used in registryTool.clientId field for associated tooling should be included
|
||||
# in this list. Client IDs are typically of the format
|
||||
# backend services, e.g. nomulus tool, EPP proxy, etc. The value in
|
||||
# registryTool.clientId field should be included in this list. Client IDs are
|
||||
# typically of the format
|
||||
# numbers-alphanumerics.apps.googleusercontent.com
|
||||
allowedOauthClientIds: []
|
||||
# GCP Identity-Aware Proxy client ID, if set up (note: this requires manual setup
|
||||
# of User objects in the database for Nomulus tool users)
|
||||
iapClientId: null
|
||||
|
||||
# Service accounts (e.g. default service account, account used by Cloud
|
||||
# Scheduler) allowed to send authenticated requests.
|
||||
allowedServiceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
|
||||
# OAuth 2.0 client ID that will be used as the audience in OIDC ID tokens sent
|
||||
# from clients (e.g. proxy, nomulus tool, cloud tasks) for authentication. The
|
||||
# same ID is the only one accepted by the regular OIDC or IAP authentication
|
||||
# mechanisms. In most cases we should use the client ID created for IAP here,
|
||||
# as it allows requests bearing a token with this audience to be accepted by
|
||||
# both IAP or regular OIDC. The clientId value in proxy config file should be
|
||||
# the same as this one.
|
||||
oauthClientId: iap-oauth-clientid
|
||||
|
||||
credentialOAuth:
|
||||
# OAuth scopes required for accessing Google APIs using the default
|
||||
|
|
|
@ -140,25 +140,13 @@ public final class TldFanoutAction implements Runnable {
|
|||
for (String tld : tlds) {
|
||||
Task task = createTask(tld, flowThruParams);
|
||||
Task createdTask = cloudTasksUtils.enqueue(queue, task);
|
||||
if (createdTask.hasAppEngineHttpRequest()) {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(),
|
||||
tld,
|
||||
createdTask.getAppEngineHttpRequest().getRelativeUri()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri());
|
||||
} else {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl());
|
||||
}
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl());
|
||||
}
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(outputPayload.toString());
|
||||
|
|
|
@ -44,7 +44,7 @@ public class AuthModule {
|
|||
// 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";
|
||||
private static final String REGULAR_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth and OIDC). */
|
||||
@Provides
|
||||
|
@ -82,8 +82,8 @@ public class AuthModule {
|
|||
@Provides
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("oauthClientId") String clientId) {
|
||||
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -32,7 +32,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
/**
|
||||
* OAuth authentication mechanism, using the OAuthService interface.
|
||||
*
|
||||
* Only OAuth version 2 is supported.
|
||||
* <p>Only OAuth version 2 is supported.
|
||||
*/
|
||||
public class OAuthAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ 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.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
|
@ -57,10 +57,10 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
|||
|
||||
protected final TokenExtractor tokenExtractor;
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
private final ImmutableSet<String> serviceAccountEmails;
|
||||
|
||||
protected OidcTokenAuthenticationMechanism(
|
||||
ImmutableList<String> serviceAccountEmails,
|
||||
ImmutableSet<String> serviceAccountEmails,
|
||||
TokenVerifier tokenVerifier,
|
||||
TokenExtractor tokenExtractor) {
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
|
@ -83,7 +83,11 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
|||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
logger.atInfo().withCause(e).log(
|
||||
"Failed OIDC verification attempt:\n%s",
|
||||
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
|
||||
? "Raw token redacted in prod"
|
||||
: rawIdToken);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String email = (String) token.getPayload().get("email");
|
||||
|
@ -95,6 +99,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
|||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
// TODO: implement caching so we don't have to look up the database for every request.
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.create(APP);
|
||||
|
@ -136,7 +141,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
|||
|
||||
@Inject
|
||||
protected IapOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@IapOidc TokenVerifier tokenVerifier,
|
||||
@IapOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
|
@ -164,7 +169,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
|||
|
||||
@Inject
|
||||
protected RegularOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@RegularOidc TokenVerifier tokenVerifier,
|
||||
@RegularOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
|
|
|
@ -14,23 +14,17 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import static com.google.common.net.HttpHeaders.PROXY_AUTHORIZATION;
|
||||
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.UrlEncodedContent;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.util.GenericData;
|
||||
import com.google.auth.oauth2.UserCredentials;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
import google.registry.util.OidcTokenUtils;
|
||||
|
||||
/**
|
||||
* Module for providing the HttpRequestFactory.
|
||||
|
@ -39,25 +33,16 @@ import java.util.Optional;
|
|||
* connections in that they don't require OAuth2 credentials, but instead require a special cookie.
|
||||
*/
|
||||
@Module
|
||||
class RequestFactoryModule {
|
||||
final class RequestFactoryModule {
|
||||
|
||||
static final int REQUEST_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Server to use if we want to manually request an IAP ID token
|
||||
*
|
||||
* <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 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"));
|
||||
private RequestFactoryModule() {}
|
||||
|
||||
@Provides
|
||||
static HttpRequestFactory provideHttpRequestFactory(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("iapClientId") Optional<String> iapClientId) {
|
||||
@Config("oauthClientId") String oauthClientId) {
|
||||
if (RegistryConfig.areServersLocal()) {
|
||||
return new NetHttpTransport()
|
||||
.createRequestFactory(
|
||||
|
@ -71,14 +56,13 @@ class RequestFactoryModule {
|
|||
request -> {
|
||||
// Use the standard credential initializer to set the Authorization header
|
||||
credentialsBundle.getHttpRequestInitializer().initialize(request);
|
||||
// If using IAP, use the refresh token to acquire an IAP-enabled ID token and use
|
||||
// that for authentication.
|
||||
if (iapClientId.isPresent()) {
|
||||
String idToken = getIdToken(credentialsBundle, iapClientId.get());
|
||||
// Set the Proxy-Authentication header so that IAP can read from it, see
|
||||
// https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_proxy-authorization_header
|
||||
request.getHeaders().set("Proxy-Authorization", "Bearer " + idToken);
|
||||
}
|
||||
// Set OIDC token as the alternative bearer token.
|
||||
request
|
||||
.getHeaders()
|
||||
.set(
|
||||
PROXY_AUTHORIZATION,
|
||||
"Bearer "
|
||||
+ OidcTokenUtils.createOidcToken(credentialsBundle, oauthClientId));
|
||||
// GAE request times out after 10 min, so here we set the timeout to 10 min. This is
|
||||
// needed to support some nomulus commands like updating premium lists that take
|
||||
// a lot of time to complete.
|
||||
|
@ -89,32 +73,4 @@ class RequestFactoryModule {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the saved desktop-app refresh token to acquire an IAP ID token.
|
||||
*
|
||||
* <p>This is lifted mostly from the Google Auth Library's {@link UserCredentials}
|
||||
* "doRefreshAccessToken" method (which is private and thus inaccessible) while adding in the
|
||||
* audience of the IAP client ID. That addition of the audience is what allows us to satisfy IAP
|
||||
* auth. See
|
||||
* https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_desktop_app for
|
||||
* more details.
|
||||
*/
|
||||
private static String getIdToken(GoogleCredentialsBundle credentialsBundle, String iapClientId)
|
||||
throws IOException {
|
||||
UserCredentials credentials = (UserCredentials) credentialsBundle.getGoogleCredentials();
|
||||
GenericData tokenRequest = new GenericData();
|
||||
tokenRequest.set("client_id", credentials.getClientId());
|
||||
tokenRequest.set("client_secret", credentials.getClientSecret());
|
||||
tokenRequest.set("refresh_token", credentials.getRefreshToken());
|
||||
tokenRequest.set("audience", iapClientId);
|
||||
tokenRequest.set("grant_type", "refresh_token");
|
||||
UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
|
||||
|
||||
HttpRequestFactory requestFactory = credentialsBundle.getHttpTransport().createRequestFactory();
|
||||
HttpRequest request = requestFactory.buildPostRequest(TOKEN_SERVER_URL, content);
|
||||
request.setParser(credentialsBundle.getJsonFactory().createJsonObjectParser());
|
||||
HttpResponse response = request.execute();
|
||||
return response.parseAs(GenericData.class).get("id_token").toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class AsyncTaskEnqueuerTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new CloudTasksHelper.TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
@ -93,7 +93,7 @@ public class AsyncTaskEnqueuerTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMultimap;
|
|||
import com.google.common.collect.LinkedListMultimap;
|
||||
import google.registry.batch.CloudTasksUtils.SerializableCloudTasksClient;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.testing.CloudTasksHelper.FakeGoogleCredentialsBundle;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.Retrier;
|
||||
|
@ -47,14 +48,14 @@ public class CloudTasksUtilsTest {
|
|||
private final LinkedListMultimap<String, String> params = LinkedListMultimap.create();
|
||||
private final SerializableCloudTasksClient mockClient = mock(SerializableCloudTasksClient.class);
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-11-08"));
|
||||
private CloudTasksUtils cloudTasksUtils =
|
||||
private final CloudTasksUtils cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
"clientId",
|
||||
FakeGoogleCredentialsBundle.create(),
|
||||
mockClient);
|
||||
|
||||
@BeforeEach
|
||||
|
@ -66,208 +67,6 @@ public class CloudTasksUtilsTest {
|
|||
.thenAnswer(invocation -> invocation.getArgument(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withEmptyParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withEmptyParams() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(100));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(100).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(1));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(1).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_createGetTasks_withNegativeDelay() {
|
||||
IllegalArgumentException thrown =
|
||||
|
@ -290,34 +89,6 @@ public class CloudTasksUtilsTest {
|
|||
assertThat(thrown).hasMessageThat().isEqualTo("Negative duration is not supported.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay("/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_illegalPath() {
|
||||
assertThrows(
|
||||
|
@ -357,22 +128,20 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -382,43 +151,39 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withEmptyParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withEmptyParams() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
|
@ -426,14 +191,13 @@ public class CloudTasksUtilsTest {
|
|||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(100));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
|
@ -446,13 +210,12 @@ public class CloudTasksUtilsTest {
|
|||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(1));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -469,13 +232,12 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -485,26 +247,24 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -514,40 +274,37 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -559,13 +316,12 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
|
@ -575,35 +331,22 @@ public class CloudTasksUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay("/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private void createOidcTasksUtils() {
|
||||
cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.of("defaultServiceAccount"),
|
||||
Optional.of("iapClientId"),
|
||||
mockClient);
|
||||
}
|
||||
|
||||
private void verifyOidcToken(Task task) {
|
||||
private static void verifyOidcToken(Task task) {
|
||||
assertThat(task.getHttpRequest().getOidcToken())
|
||||
.isEqualTo(
|
||||
OidcToken.newBuilder()
|
||||
.setServiceAccountEmail("defaultServiceAccount")
|
||||
.setAudience("iapClientId")
|
||||
.setServiceAccountEmail("service@account.com")
|
||||
.setAudience("clientId")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -305,7 +305,7 @@ public class RelockDomainActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.param(
|
||||
RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM,
|
||||
|
|
|
@ -136,7 +136,7 @@ public class ResaveEntityActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
|
|
@ -464,7 +464,7 @@ public class RdePipelineTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"brda",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/brdaCopy")
|
||||
.path("/_dr/task/brdaCopy")
|
||||
.service("backend")
|
||||
.param("tld", "soy")
|
||||
.param("watermark", now.toString())
|
||||
|
@ -472,7 +472,7 @@ public class RdePipelineTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-upload",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeUpload")
|
||||
.path("/_dr/task/rdeUpload")
|
||||
.service("backend")
|
||||
.param("tld", "soy")
|
||||
.param("prefix", "rde-job/"));
|
||||
|
|
|
@ -91,14 +91,14 @@ class TldFanoutActionTest {
|
|||
.map(
|
||||
namespace ->
|
||||
new TaskMatcher()
|
||||
.url(ENDPOINT)
|
||||
.path(ENDPOINT)
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
.param("tld", namespace))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
private void assertTaskWithoutTld() {
|
||||
cloudTasksHelper.assertTasksEnqueued(QUEUE, new TaskMatcher().url(ENDPOINT));
|
||||
cloudTasksHelper.assertTasksEnqueued(QUEUE, new TaskMatcher().path(ENDPOINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -211,7 +211,7 @@ class TldFanoutActionTest {
|
|||
void testSuccess_additionalArgsFlowThroughToPostParams() {
|
||||
run(getParamsMap("forEachTestTld", "", "newkey", "newval"));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE, new TaskMatcher().url("/the/servlet").param("newkey", "newval"));
|
||||
QUEUE, new TaskMatcher().path("/the/servlet").param("newkey", "newval"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -224,9 +224,9 @@ class TldFanoutActionTest {
|
|||
String expectedResponse =
|
||||
String.format(
|
||||
"OK: Launched the following 3 tasks in queue the-queue\n"
|
||||
+ "- Task: '%s', tld: 'com', endpoint: '/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'net', endpoint: '/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'org', endpoint: '/the/servlet'\n",
|
||||
+ "- Task: '%s', tld: 'com', endpoint: 'https://backend.example.com/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'net', endpoint: 'https://backend.example.com/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'org', endpoint: 'https://backend.example.com/the/servlet'\n",
|
||||
taskList.get(0).getName(), taskList.get(1).getName(), taskList.get(2).getName());
|
||||
assertThat(response.getPayload()).isEqualTo(expectedResponse);
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class TldFanoutActionTest {
|
|||
String expectedResponse =
|
||||
String.format(
|
||||
"OK: Launched the following 1 tasks in queue the-queue\n"
|
||||
+ "- Task: '%s', tld: '', endpoint: '/the/servlet'\n",
|
||||
+ "- Task: '%s', tld: '', endpoint: 'https://backend.example.com/the/servlet'\n",
|
||||
taskList.get(0).getName());
|
||||
assertThat(response.getPayload()).isEqualTo(expectedResponse);
|
||||
}
|
||||
|
|
|
@ -294,7 +294,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
@ -305,7 +305,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
@ -333,7 +333,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
@ -344,7 +344,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
@ -370,7 +370,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
@ -381,7 +381,7 @@ public class PublishDnsUpdatesActionTest {
|
|||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
|
|
@ -225,7 +225,7 @@ public class ReadDnsRefreshRequestsActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"dns-publish",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishDnsUpdates")
|
||||
.path("/_dr/task/publishDnsUpdates")
|
||||
.service("BACKEND")
|
||||
.param("tld", "tld")
|
||||
.param("dnsWriter", "FooWriter")
|
||||
|
@ -236,7 +236,7 @@ public class ReadDnsRefreshRequestsActionTest {
|
|||
.param("domains", "domain.tld,future.tld")
|
||||
.param("hosts", "ns1.domain.tld"),
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishDnsUpdates")
|
||||
.path("/_dr/task/publishDnsUpdates")
|
||||
.service("BACKEND")
|
||||
.param("tld", "tld")
|
||||
.param("dnsWriter", "BarWriter")
|
||||
|
|
|
@ -304,7 +304,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
|
|
@ -520,7 +520,7 @@ class DomainTransferRequestFlowTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
|
|
@ -207,7 +207,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_HOST_RENAME,
|
||||
new TaskMatcher()
|
||||
.url(RefreshDnsOnHostRenameAction.PATH)
|
||||
.path(RefreshDnsOnHostRenameAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(PARAM_HOST_KEY, renamedHost.createVKey().stringify()));
|
||||
|
|
|
@ -227,7 +227,7 @@ public class RdeUploadActionTest {
|
|||
action, Tld.get("lol"), standardSeconds(23), CursorType.RDE_UPLOAD, standardDays(1));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher().url(RdeReportAction.PATH).param(RequestParameters.PARAM_TLD, "lol"));
|
||||
new TaskMatcher().path(RdeReportAction.PATH).param(RequestParameters.PARAM_TLD, "lol"));
|
||||
verifyNoMoreInteractions(runner);
|
||||
}
|
||||
|
||||
|
@ -244,7 +244,7 @@ public class RdeUploadActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url(RdeReportAction.PATH)
|
||||
.path(RdeReportAction.PATH)
|
||||
.param(RequestParameters.PARAM_TLD, "lol")
|
||||
.param(RdeModule.PARAM_PREFIX, "job-name/"));
|
||||
verifyNoMoreInteractions(runner);
|
||||
|
|
|
@ -74,7 +74,7 @@ class GenerateInvoicesActionTest extends BeamActionTestBase {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"beam-reporting",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishInvoices")
|
||||
.path("/_dr/task/publishInvoices")
|
||||
.method(HttpMethod.POST)
|
||||
.param("jobId", "jobid")
|
||||
.param("yearMonth", "2017-10")
|
||||
|
|
|
@ -83,7 +83,7 @@ class PublishInvoicesActionTest {
|
|||
verify(emailUtils).emailOverallInvoice();
|
||||
TaskMatcher matcher =
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/copyDetailReports")
|
||||
.path("/_dr/task/copyDetailReports")
|
||||
.method(HttpMethod.POST)
|
||||
.param("yearMonth", "2017-10");
|
||||
cloudTasksHelper.assertTasksEnqueued("retryable-cron-tasks", matcher);
|
||||
|
|
|
@ -78,7 +78,7 @@ class IcannReportingStagingActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"retryable-cron-tasks",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/icannReportingUpload")
|
||||
.path("/_dr/task/icannReportingUpload")
|
||||
.method(HttpMethod.POST)
|
||||
.scheduleTime(clock.nowUtc().plus(Duration.standardMinutes(2))));
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"beam-reporting",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishSpec11")
|
||||
.path("/_dr/task/publishSpec11")
|
||||
.method(HttpMethod.POST)
|
||||
.param("jobId", "jobid")
|
||||
.param("date", "2018-06-11")
|
||||
|
|
|
@ -29,7 +29,7 @@ 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 com.google.common.collect.ImmutableSet;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
@ -54,8 +54,8 @@ 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 static final ImmutableSet<String> serviceAccounts =
|
||||
ImmutableSet.of("service@email.test", "email@service.goog");
|
||||
|
||||
private final Payload payload = new Payload();
|
||||
private final User user =
|
||||
|
@ -222,9 +222,16 @@ public class OidcTokenAuthenticationMechanismTest {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("serviceAccountEmails")
|
||||
ImmutableList<String> provideServiceAccountEmails() {
|
||||
@Config("allowedServiceAccountEmails")
|
||||
ImmutableSet<String> provideAllowedServiceAccountEmails() {
|
||||
return serviceAccounts;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("oauthClientId")
|
||||
String provideOauthClientId() {
|
||||
return "client-id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ import static google.registry.util.DiffUtils.prettyPrintEntityDeepDiff;
|
|||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.HttpRequest;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Joiner;
|
||||
|
@ -45,6 +47,7 @@ import dagger.Module;
|
|||
import dagger.Provides;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
|
@ -58,12 +61,13 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -104,8 +108,8 @@ public class CloudTasksHelper implements Serializable {
|
|||
clock,
|
||||
PROJECT_ID,
|
||||
LOCATION_ID,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
"client.id",
|
||||
FakeGoogleCredentialsBundle.create(),
|
||||
new FakeCloudTasksClient());
|
||||
testTasks.put(instanceId, Multimaps.synchronizedListMultimap(LinkedListMultimap.create()));
|
||||
}
|
||||
|
@ -219,18 +223,35 @@ public class CloudTasksHelper implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public static class FakeGoogleCredentialsBundle extends GoogleCredentialsBundle {
|
||||
|
||||
private static final long serialVersionUID = -3251343247195058893L;
|
||||
|
||||
private static final FakeGoogleCredentialsBundle INSTANCE = new FakeGoogleCredentialsBundle();
|
||||
|
||||
public static GoogleCredentialsBundle create() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private FakeGoogleCredentialsBundle() {
|
||||
super(new GoogleCredentials(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serviceAccount() {
|
||||
return "service@account.com";
|
||||
}
|
||||
}
|
||||
|
||||
/** An adapter to clean up a {@link Task} for ease of matching. */
|
||||
private static class MatchableTask extends ImmutableObject {
|
||||
|
||||
private static final Pattern HOSTNAME_PATTERN =
|
||||
Pattern.compile("(?<=https://)[a-z]+(?=\\.example\\.com)");
|
||||
String taskName;
|
||||
String service;
|
||||
// App Engine TaskOption methods default to "POST". This isn't obvious from the code, so we
|
||||
// default it to POST here so that we don't accidentally create an entry with a GET method when
|
||||
// converting to CloudTasksUtils, which requires that the method be specified explicitly.
|
||||
// Should we ever actually want to do a GET, we'll likewise have to set this explicitly for the
|
||||
// tests.
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
String url;
|
||||
HttpMethod method;
|
||||
String path;
|
||||
Timestamp scheduleTime;
|
||||
Multimap<String, String> headers = ArrayListMultimap.create();
|
||||
Multimap<String, String> params = ArrayListMultimap.create();
|
||||
|
@ -238,24 +259,24 @@ public class CloudTasksHelper implements Serializable {
|
|||
MatchableTask() {}
|
||||
|
||||
MatchableTask(Task task) {
|
||||
HttpRequest request = task.getHttpRequest();
|
||||
taskName = task.getName();
|
||||
String url = request.getUrl();
|
||||
// URI class provides helper method to extract query parameters.
|
||||
URI uri;
|
||||
try {
|
||||
// Construct a fake full URI for parsing purpose. The relative URI must start with a slash.
|
||||
uri =
|
||||
new URI(
|
||||
String.format(
|
||||
"https://nomulus.foo%s", task.getAppEngineHttpRequest().getRelativeUri()));
|
||||
uri = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
taskName = task.getName();
|
||||
service =
|
||||
Ascii.toLowerCase(task.getAppEngineHttpRequest().getAppEngineRouting().getService());
|
||||
method = task.getAppEngineHttpRequest().getHttpMethod();
|
||||
url = uri.getPath();
|
||||
Matcher hostnameMatcher = HOSTNAME_PATTERN.matcher(url);
|
||||
assertThat(hostnameMatcher.find()).isTrue();
|
||||
service = Ascii.toLowerCase(hostnameMatcher.group());
|
||||
path = url.substring(String.format("https://%s.example.com", service).length());
|
||||
method = request.getHttpMethod();
|
||||
scheduleTime = task.getScheduleTime();
|
||||
ImmutableMultimap.Builder<String, String> headerBuilder = new ImmutableMultimap.Builder<>();
|
||||
task.getAppEngineHttpRequest()
|
||||
request
|
||||
.getHeadersMap()
|
||||
.forEach(
|
||||
(key, value) -> {
|
||||
|
@ -269,14 +290,13 @@ public class CloudTasksHelper implements Serializable {
|
|||
// where parameters are not properly URL-encoded); it always does a best-effort parse.
|
||||
if (method == HttpMethod.GET && uri.getQuery() != null) {
|
||||
paramBuilder.putAll(UriParameters.parse(uri.getQuery()));
|
||||
} else if (method == HttpMethod.POST && !task.getAppEngineHttpRequest().getBody().isEmpty()) {
|
||||
} else if (method == HttpMethod.POST && !request.getBody().isEmpty()) {
|
||||
assertThat(
|
||||
headers.containsEntry(
|
||||
Ascii.toLowerCase(HttpHeaders.CONTENT_TYPE), MediaType.FORM_DATA.toString()))
|
||||
.isTrue();
|
||||
paramBuilder.putAll(
|
||||
UriParameters.parse(
|
||||
task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)));
|
||||
UriParameters.parse(request.getBody().toString(StandardCharsets.UTF_8)));
|
||||
}
|
||||
params = paramBuilder.build();
|
||||
}
|
||||
|
@ -286,7 +306,7 @@ public class CloudTasksHelper implements Serializable {
|
|||
builder.put("taskName", taskName);
|
||||
builder.put("method", method);
|
||||
builder.put("service", service);
|
||||
builder.put("url", url);
|
||||
builder.put("path", path);
|
||||
builder.put("headers", headers);
|
||||
builder.put("params", params);
|
||||
builder.put("scheduleTime", scheduleTime);
|
||||
|
@ -310,8 +330,8 @@ public class CloudTasksHelper implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TaskMatcher url(String url) {
|
||||
expected.url = url;
|
||||
public TaskMatcher path(String path) {
|
||||
expected.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -371,7 +391,7 @@ public class CloudTasksHelper implements Serializable {
|
|||
public boolean test(@Nonnull Task task) {
|
||||
MatchableTask actual = new MatchableTask(task);
|
||||
return (expected.taskName == null || Objects.equals(expected.taskName, actual.taskName))
|
||||
&& (expected.url == null || Objects.equals(expected.url, actual.url))
|
||||
&& (expected.path == null || Objects.equals(expected.path, actual.path))
|
||||
&& (expected.method == null || Objects.equals(expected.method, actual.method))
|
||||
&& (expected.service == null || Objects.equals(expected.service, actual.service))
|
||||
&& (expected.scheduleTime == null
|
||||
|
|
|
@ -252,7 +252,7 @@ class NordnUploadActionTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
NordnVerifyAction.QUEUE,
|
||||
new TaskMatcher()
|
||||
.url(NordnVerifyAction.PATH)
|
||||
.path(NordnVerifyAction.PATH)
|
||||
.param(NordnVerifyAction.NORDN_URL_PARAM, LOCATION_URL)
|
||||
.param(RequestParameters.PARAM_TLD, "tld")
|
||||
.header(CONTENT_TYPE, FORM_DATA.toString()));
|
||||
|
|
|
@ -259,7 +259,7 @@ public final class DomainLockUtilsTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(
|
||||
|
@ -481,7 +481,7 @@ public final class DomainLockUtilsTest {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new CloudTasksHelper.TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(
|
||||
|
|
|
@ -73,7 +73,7 @@ final class GcpProjectConnectionTest {
|
|||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
getStreamingContent().writeTo(output);
|
||||
output.close();
|
||||
return new String(output.toByteArray(), UTF_8);
|
||||
return output.toString(UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ final class GcpProjectConnectionTest {
|
|||
.isEqualTo("MyContent");
|
||||
assertThat(httpTransport.method).isEqualTo("GET");
|
||||
assertThat(httpTransport.url)
|
||||
.isEqualTo("https://localhost/my/path?query&key1=value1&key2=value2");
|
||||
.isEqualTo("https://tools.example.com/my/path?query&key1=value1&key2=value2");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("Cache-Control", "no-cache");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("x-requested-with", "RegistryTool");
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ final class GcpProjectConnectionTest {
|
|||
.isEqualTo("MyContent");
|
||||
assertThat(httpTransport.method).isEqualTo("POST");
|
||||
assertThat(httpTransport.url)
|
||||
.isEqualTo("https://localhost/my/path?query&key1=value1&key2=value2");
|
||||
.isEqualTo("https://tools.example.com/my/path?query&key1=value1&key2=value2");
|
||||
assertThat(lowLevelHttpRequest.getContentType()).isEqualTo("text/plain; charset=utf-8");
|
||||
assertThat(lowLevelHttpRequest.getContentString()).isEqualTo("some data");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("Cache-Control", "no-cache");
|
||||
|
@ -130,7 +130,7 @@ final class GcpProjectConnectionTest {
|
|||
"/my/path?query", ImmutableMap.of("string", "value1", "bool", true)))
|
||||
.containsExactly("key", "value");
|
||||
assertThat(httpTransport.method).isEqualTo("POST");
|
||||
assertThat(httpTransport.url).isEqualTo("https://localhost/my/path?query");
|
||||
assertThat(httpTransport.url).isEqualTo("https://tools.example.com/my/path?query");
|
||||
assertThat(lowLevelHttpRequest.getContentType()).isEqualTo("application/json; charset=utf-8");
|
||||
assertThat(lowLevelHttpRequest.getContentString())
|
||||
.isEqualTo("{\"string\":\"value1\",\"bool\":true}");
|
||||
|
|
|
@ -187,7 +187,7 @@ public class GenerateEscrowDepositCommandTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "true")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
|
@ -204,7 +204,7 @@ public class GenerateEscrowDepositCommandTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
|
@ -221,7 +221,7 @@ public class GenerateEscrowDepositCommandTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("lenient", "false")
|
||||
.param("mode", "THIN")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
|
@ -237,7 +237,7 @@ public class GenerateEscrowDepositCommandTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "FULL")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
|
@ -259,7 +259,7 @@ public class GenerateEscrowDepositCommandTest
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z,2017-01-02T00:00:00.000Z")
|
||||
|
|
|
@ -35,7 +35,6 @@ import com.google.auth.oauth2.UserCredentials;
|
|||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -65,7 +64,7 @@ public class RequestFactoryModuleTest {
|
|||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = true;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, Optional.empty());
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id");
|
||||
HttpRequestInitializer initializer = factory.getInitializer();
|
||||
assertThat(initializer).isNotNull();
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
|
@ -79,29 +78,7 @@ public class RequestFactoryModuleTest {
|
|||
@Test
|
||||
void test_provideHttpRequestFactory_remote() throws Exception {
|
||||
when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer);
|
||||
// Make sure that example.com creates a request factory with the UNITTEST client id but no
|
||||
boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal;
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, Optional.empty());
|
||||
HttpRequestInitializer initializer = factory.getInitializer();
|
||||
assertThat(initializer).isNotNull();
|
||||
// HttpRequestFactory#buildGetRequest() calls initialize() once.
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
verify(httpRequestInitializer).initialize(request);
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verifyNoMoreInteractions(httpRequestInitializer);
|
||||
} finally {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_provideHttpRequestFactory_remote_withIap() throws Exception {
|
||||
when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer);
|
||||
// Mock the request/response to/from the IAP server requesting an ID token
|
||||
// Mock the request/response to/from the OIDC server requesting an ID token
|
||||
UserCredentials mockUserCredentials = mock(UserCredentials.class);
|
||||
when(credentialsBundle.getGoogleCredentials()).thenReturn(mockUserCredentials);
|
||||
HttpTransport mockTransport = mock(HttpTransport.class);
|
||||
|
@ -114,17 +91,16 @@ public class RequestFactoryModuleTest {
|
|||
HttpResponse mockResponse = mock(HttpResponse.class);
|
||||
when(mockPostRequest.execute()).thenReturn(mockResponse);
|
||||
GenericData genericDataResponse = new GenericData();
|
||||
genericDataResponse.set("id_token", "iapIdToken");
|
||||
genericDataResponse.set("id_token", "oidc.token");
|
||||
when(mockResponse.parseAs(GenericData.class)).thenReturn(genericDataResponse);
|
||||
|
||||
boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal;
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(
|
||||
credentialsBundle, Optional.of("iapClientId"));
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId");
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer iapIdToken");
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token");
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verify(httpRequestInitializer).initialize(request);
|
||||
|
|
|
@ -26,13 +26,13 @@ public class ServiceConnectionTest {
|
|||
void testServerUrl_notCanary() {
|
||||
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, false);
|
||||
String serverUrl = connection.getServer().toString();
|
||||
assertThat(serverUrl).isEqualTo("https://localhost"); // See default-config.yaml
|
||||
assertThat(serverUrl).isEqualTo("https://default.example.com"); // See default-config.yaml
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerUrl_canary() {
|
||||
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, true);
|
||||
String serverUrl = connection.getServer().toString();
|
||||
assertThat(serverUrl).isEqualTo("https://nomulus-dot-localhost");
|
||||
assertThat(serverUrl).isEqualTo("https://nomulus-dot-default.example.com");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class RegistrarSettingsActionTest extends RegistrarSettingsActionTestCase {
|
|||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"sheet",
|
||||
new TaskMatcher()
|
||||
.url(SyncRegistrarsSheetAction.PATH)
|
||||
.path(SyncRegistrarsSheetAction.PATH)
|
||||
.service("Backend")
|
||||
.method(HttpMethod.GET));
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "SUCCESS");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue