All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.tools.remoteapi.RemoteApiOptions Maven / Gradle / Ivy

/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.appengine.tools.remoteapi;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.compute.ComputeCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.apphosting.api.ApiProxy;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * A mutable object containing settings for installing the remote API.
 *
 * 

Example for connecting to a development app server:

* *
 * RemoteApiOptions options = new RemoteApiOptions()
 *     .server("localhost", 8888),
 *     .useDevelopmentServerCredential();
 * 
* *

Example for connecting to a deployed app:

* *
 * RemoteApiOptions options = new RemoteApiOptions()
 *     .server("myappid.appspot.com", 443),
 *     .useApplicationDefaultCredential();
 * 
* *

* The options should be passed to {@link RemoteApiInstaller#install}. *

* */ public class RemoteApiOptions { private static final ImmutableList OAUTH_SCOPES = ImmutableList.of( "https://www.googleapis.com/auth/appengine.apis", "https://www.googleapis.com/auth/userinfo.email"); private static final String LOCAL_USER = "[email protected]"; private static final String LOCAL_PASSWORD = ""; private String hostname; private int port; private String userEmail; private String password; private String credentialsToReuse; private String remoteApiPath = "/remote_api"; private int maxConcurrentRequests = 5; private int datastoreQueryFetchSize = 500; private int maxHttpResponseSize = 33 * 1024 * 1024; // public methods that populate oauthCredential must also populate // httpTransport (preferably by calling getOrCreateHttpTransport()). private Credential oauthCredential; private HttpTransport httpTransport; public RemoteApiOptions() {} RemoteApiOptions(RemoteApiOptions original) { this.hostname = original.hostname; this.port = original.port; this.userEmail = original.userEmail; this.password = original.password; this.credentialsToReuse = original.credentialsToReuse; this.remoteApiPath = original.remoteApiPath; this.maxConcurrentRequests = original.maxConcurrentRequests; this.datastoreQueryFetchSize = original.datastoreQueryFetchSize; this.maxHttpResponseSize = original.maxHttpResponseSize; this.oauthCredential = original.oauthCredential; this.httpTransport = original.httpTransport; } /** * Sets the host and port port where we will connect. */ public RemoteApiOptions server(String newHostname, int newPort) { hostname = newHostname; port = newPort; return this; } /** * Sets a username and password to be used for logging in via the * ClientLogin API. Overrides any previously-provided credentials. * * @deprecated Use {@link #useApplicationDefaultCredential} or * {@link useServiceAccountCredential} instead. */ @Deprecated public RemoteApiOptions credentials(String newUserEMail, String newPassword) { userEmail = newUserEMail; password = newPassword; credentialsToReuse = null; oauthCredential = null; return this; } /** * Reuses credentials from another AppEngineClient. Credentials can only * be reused from a client with the same hostname and user. Overrides any * previously-provided credentials. * @param newUserEmail the email address of the user we want to log in as. * @param serializedCredentials a string returned by calling * {@link AppEngineClient#serializeCredentials} on the previous client */ public RemoteApiOptions reuseCredentials(String newUserEmail, String serializedCredentials) { userEmail = newUserEmail; password = null; credentialsToReuse = serializedCredentials; oauthCredential = null; return this; } /** * Use a Compute Engine credential for authentication. Overrides any * previously-provided credentials. * * @return this {@code RemoteApiOptions} instance * @deprecated Use {@link #useApplicationDefaultCredential}. */ @Deprecated public RemoteApiOptions useComputeEngineCredential() { // Attempt to eagerly populate the OAuth credential. This simplifies the // subsequent process of constructing a client and means we fail fast // if there's a problem getting the credential. try { HttpTransport transport = getOrCreateHttpTransportForOAuth(); // Try to connect using Google Compute Engine service account credentials. ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); // Force token refresh to verify that we are running on Google Compute Engine. credential.refreshToken(); setOAuthCredential(credential); } catch (IOException|GeneralSecurityException e) { throw new RuntimeException("Failed to acquire Google Compute Engine credential.", e); } return this; } /** * Use a Google Application Default credential for authentication. Overrides any * previously-provided credentials. * * @return this {@code RemoteApiOptions} instance. * @see * Application Default Credentials */ public RemoteApiOptions useApplicationDefaultCredential() { try { getOrCreateHttpTransportForOAuth(); // Necessary to populate the http transport. GoogleCredential credential = GoogleCredential.getApplicationDefault(); credential = credential.createScoped(OAUTH_SCOPES); credential.refreshToken(); setOAuthCredential(credential); } catch (IOException|GeneralSecurityException e) { throw new RuntimeException("Failed to acquire Google Application Default credential.", e); } return this; } /** * Use a service account credential. Overrides any previously-provided * credentials. * * @param serviceAccountId service account ID (typically an e-mail address) * @param p12PrivateKeyFile p12 file containing a private key to use with the service account * * @return this {@code RemoteApiOptions} instance */ public RemoteApiOptions useServiceAccountCredential(String serviceAccountId, String p12PrivateKeyFile) { // Attempt to eagerly populate the OAuth credential. This simplifies the // subsequent process of constructing a client and means we fail fast // if there's a problem getting the credential. try { Credential credential = getCredentialBuilder(serviceAccountId) .setServiceAccountPrivateKeyFromP12File(new File(p12PrivateKeyFile)) .build(); setOAuthCredential(credential); } catch (IOException|GeneralSecurityException e) { throw new RuntimeException("Failed to build service account credential.", e); } return this; } /** * Use a service account credential. Overrides any previously-provided * credentials. * * @param serviceAccountId service account ID (typically an e-mail address) * @param privateKey private key to use with the service account * * @return this {@code RemoteApiOptions} instance */ public RemoteApiOptions useServiceAccountCredential(String serviceAccountId, PrivateKey privateKey) { // Attempt to eagerly populate the OAuth credential. This simplifies the // subsequent process of constructing a client and means we fail fast // if there's a problem getting the credential. try { Credential credential = getCredentialBuilder(serviceAccountId) .setServiceAccountPrivateKey(privateKey) .build(); setOAuthCredential(credential); } catch (IOException|GeneralSecurityException e) { throw new RuntimeException("Failed to build service account credential.", e); } return this; } /** * Use credentials appropriate for talking to the Development Server. Overrides any * previously-provided credentials. * * @return this {@code RemoteApiOptions} instance */ // NOTE: This method is a friendlier alternative to calling // credentials("[email protected]", "non-password") to talk to a devappserver. // It's especially helpful for developers using OAuth credentials in their // prod environment, as these developers will not know a password (or possibly // even the email address) for the service account they're using. // // We use cookie-based login rather than OAuth-based login because the latter // requires extra configuration when starting the devappserver (to force // OAuth requests to be treated as admin requests). public RemoteApiOptions useDevelopmentServerCredential() { credentials(LOCAL_USER, LOCAL_PASSWORD); return this; } private GoogleCredential.Builder getCredentialBuilder( String serviceAccountId) throws GeneralSecurityException, IOException { HttpTransport transport = getOrCreateHttpTransportForOAuth(); JacksonFactory jsonFactory = new JacksonFactory(); return new GoogleCredential.Builder() .setTransport(transport) .setJsonFactory(jsonFactory) .setServiceAccountId(serviceAccountId) .setServiceAccountScopes(OAUTH_SCOPES); } // @VisibleForTesting // NOTE: This method only exists to allow tests to simulate // {@link #useApplicationDefaultCredential}. RemoteApiOptions useGoogleCredentialStream(InputStream stream) { try { getOrCreateHttpTransportForOAuth(); // Necessary to populate the http transport. GoogleCredential credential = GoogleCredential.fromStream(stream); credential = credential.createScoped(OAUTH_SCOPES); credential.refreshToken(); setOAuthCredential(credential); } catch (IOException|GeneralSecurityException e) { throw new RuntimeException("Failed to acquire Google credential.", e); } return this; } // @VisibleForTesting // NOTE: This method might be convenient outside of tests, but // because Credential is repackaged in the SDK, it's not feasible to pass // one in from user code. RemoteApiOptions oauthCredential(Credential oauthCredential) { setOAuthCredential(oauthCredential); return this; } private void setOAuthCredential(Credential oauthCredential) { userEmail = null; password = null; credentialsToReuse = null; this.oauthCredential = oauthCredential; } // @VisibleForTesting RemoteApiOptions httpTransport(HttpTransport httpTransport) { this.httpTransport = httpTransport; return this; } /** * Sets the path used to access the remote API. If not set, the default * is /remote_api. */ public RemoteApiOptions remoteApiPath(String newPath) { remoteApiPath = newPath; return this; } /** * This parameter controls the maximum number of async API requests that will be * in flight at once. Each concurrent request will likely be handled by a separate * instance of your App. Having more instances increases throughput but may * result in errors due to exceeding quota. Defaults to 5. */ public RemoteApiOptions maxConcurrentRequests(int newValue) { maxConcurrentRequests = newValue; return this; } /** * When executing a datastore query, this is the number of results to fetch * per HTTP request. Increasing this value will reduce the number of round trips * when running large queries, but too high a value can be wasteful when not * all results are needed. Defaults to 500. * *

(This value can be overridden by the code using the datastore API.)

*/ public RemoteApiOptions datastoreQueryFetchSize(int newValue) { datastoreQueryFetchSize = newValue; return this; } /** * When making a remote call, this is the maximum size of the HTTP response. * The default is 33M. Normally there's no reason to change this. This * setting has no effect when running in an App Engine container. */ public RemoteApiOptions maxHttpResponseSize(int newValue) { maxHttpResponseSize = newValue; return this; } public RemoteApiOptions copy() { return new RemoteApiOptions(this); } // === getters === /** * Create an {@link HttpTransport} appropriate to this environment or return * the one that's already been created. This method ensures that the * determination of whether we're running in App Engine happens early * (specifically, before the Remote API has been installed) and that said * determination is remembered. */ private HttpTransport getOrCreateHttpTransportForOAuth() throws IOException, GeneralSecurityException { if (httpTransport != null) { return httpTransport; } if (ApiProxy.getCurrentEnvironment() == null) { // Running outside of App Engine. httpTransport = GoogleNetHttpTransport.newTrustedTransport(); } else { httpTransport = new UrlFetchTransport(); } return httpTransport; } HttpTransport getHttpTransport() { return httpTransport; } public String getHostname() { return hostname; } public int getPort() { return port; } public String getUserEmail() { return userEmail; } public String getPassword() { return password; } public String getCredentialsToReuse() { return credentialsToReuse; } Credential getOAuthCredential() { return oauthCredential; } public String getRemoteApiPath() { return remoteApiPath; } public int getMaxConcurrentRequests() { return maxConcurrentRequests; } public int getDatastoreQueryFetchSize() { return datastoreQueryFetchSize; } public int getMaxHttpResponseSize() { return maxHttpResponseSize; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy