Properly create and use default credential (#1818)

* Properly create and use default credential

This PR consists of the following changes:

- Stopped adding scopes to the default credential when using it to access other
  non-workspace GCP APIs. Scopes are not needed here.

- Started applying scopes to the default credential when using to access
  Drive and Sheets APIs.
  - Upgraded Drive access from the deprecated credential lib to the
    up-to-date one
  - Switched Sheet access from the exported json credential to the
    scoped default credential.

This PR requires that the affected files be writable to the default
service account (project-name@appspot.gserviceaccount.com) of the
project.

- This is already the case for exported files (premium terms, reserved
  terms, and domain list).

- The registrar sync sheets in alpha, sandbox, and production have been
  updated with the new permissions.

All impacted operations have been tested in alpha.

* Properly create and use default credential

This PR consists of the following changes:

- Added a new method to generate scope-less default credential when using it to
  access other non-workspace GCP APIs. Scopes are not needed here.

  - Started to use the new credential in the SecreteManager.
  - Will migrate other usages to this new credential gradually.
  - Marked the old DefaultCredential as deprecated.

- Started applying scopes to the default credential when using to access Drive
  and Sheets APIs.

  - Upgraded Drive access from the deprecated credentials lib
  - Switched Sheet access from the exported json credential to the scoped
    default credential.

This PR requires that the affected files be writable to the default service
account (project-name@appspot.gserviceaccount.com) of the project.

- This is already the case for exported files (premium terms, reserved terms,
  and domain list).

- The registrar sync sheets in alpha, sandbox, and production have been
  updated with the new permissions.

All impacted operations have been tested in alpha.
This commit is contained in:
Weimin Yu 2022-10-18 20:20:36 -04:00 committed by GitHub
parent 5c9c59cea1
commit 2bc46bba1f
5 changed files with 63 additions and 29 deletions

View file

@ -16,7 +16,6 @@ package google.registry.config;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import dagger.Module; import dagger.Module;
@ -37,6 +36,36 @@ import javax.inject.Singleton;
@Module @Module
public abstract class CredentialModule { public abstract class CredentialModule {
/**
* Provides a {@link GoogleCredentialsBundle} backed by the application default credential from
* the Google Cloud Runtime. This credential may be used to access GCP APIs that are NOT part of
* the Google Workspace.
*
* <p>The credential returned by the Cloud Runtime depends on the runtime environment:
*
* <ul>
* <li>On App Engine, returns a scope-less {@code ComputeEngineCredentials} for
* PROJECT_ID@appspot.gserviceaccount.com
* <li>On Compute Engine, returns a scope-less {@code ComputeEngineCredentials} for
* PROJECT_NUMBER-compute@developer.gserviceaccount.com
* <li>On end user host, this returns the credential downloaded by gcloud. Please refer to <a
* href="https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login">Cloud
* SDK documentation</a> for details.
* </ul>
*/
@ApplicationDefaultCredential
@Provides
@Singleton
public static GoogleCredentialsBundle provideApplicationDefaultCredential() {
GoogleCredentials credential;
try {
credential = GoogleCredentials.getApplicationDefault();
} catch (IOException e) {
throw new RuntimeException(e);
}
return GoogleCredentialsBundle.create(credential);
}
/** /**
* Provides the default {@link GoogleCredentialsBundle} from the Google Cloud runtime. * Provides the default {@link GoogleCredentialsBundle} from the Google Cloud runtime.
* *
@ -70,26 +99,19 @@ public abstract class CredentialModule {
} }
/** /**
* Provides the default {@link GoogleCredential} from the Google Cloud runtime for G Suite * Provides a {@link GoogleCredentialsBundle} for accessing Google Workspace APIs, such as Drive
* Drive API. * and Sheets.
* TODO(b/138195359): Deprecate this credential once we figure out how to use
* {@link GoogleCredentials} for G Suite Drive API.
*/ */
@GSuiteDriveCredential @GoogleWorkspaceCredential
@Provides @Provides
@Singleton @Singleton
public static GoogleCredential provideGSuiteDriveCredential( public static GoogleCredentialsBundle provideGSuiteDriveCredential(
@ApplicationDefaultCredential GoogleCredentialsBundle applicationDefaultCredential,
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes) { @Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes) {
GoogleCredential credential; GoogleCredentials credential = applicationDefaultCredential.getGoogleCredentials();
try { // Although credential is scope-less, its `createScopedRequired` method still returns false.
credential = GoogleCredential.getApplicationDefault(); credential = credential.createScoped(requiredScopes);
} catch (IOException e) { return GoogleCredentialsBundle.create(credential);
throw new RuntimeException(e);
}
if (credential.createScopedRequired()) {
credential = credential.createScoped(requiredScopes);
}
return credential;
} }
/** /**
@ -136,18 +158,24 @@ public abstract class CredentialModule {
.createScoped(requiredScopes)); .createScoped(requiredScopes));
} }
/** Dagger qualifier for the scope-less Application Default Credential. */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationDefaultCredential {}
/** Dagger qualifier for the Application Default Credential. */ /** Dagger qualifier for the Application Default Credential. */
@Qualifier @Qualifier
@Documented @Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Deprecated // Switching to @ApplicationDefaultCredential
public @interface DefaultCredential {} public @interface DefaultCredential {}
/** Dagger qualifier for the credential for Google Workspace APIs. */
/** Dagger qualifier for the credential for G Suite Drive API. */
@Qualifier @Qualifier
@Documented @Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface GSuiteDriveCredential {} public @interface GoogleWorkspaceCredential {}
/** /**
* Dagger qualifier for a credential from a service account's JSON key, to be used in non-request * Dagger qualifier for a credential from a service account's JSON key, to be used in non-request

View file

@ -14,16 +14,16 @@
package google.registry.export; package google.registry.export;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.drive.Drive; import com.google.api.services.drive.Drive;
import dagger.Component; import dagger.Component;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.config.CredentialModule; import google.registry.config.CredentialModule;
import google.registry.config.CredentialModule.GSuiteDriveCredential; import google.registry.config.CredentialModule.GoogleWorkspaceCredential;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule; import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.storage.drive.DriveConnection; import google.registry.storage.drive.DriveConnection;
import google.registry.util.GoogleCredentialsBundle;
import javax.inject.Singleton; import javax.inject.Singleton;
/** Dagger module for Google {@link Drive} service connection objects. */ /** Dagger module for Google {@link Drive} service connection objects. */
@ -32,13 +32,13 @@ public final class DriveModule {
@Provides @Provides
static Drive provideDrive( static Drive provideDrive(
@GSuiteDriveCredential GoogleCredential googleCredential, @GoogleWorkspaceCredential GoogleCredentialsBundle googleCredential,
@Config("projectId") String projectId) { @Config("projectId") String projectId) {
return new Drive.Builder( return new Drive.Builder(
googleCredential.getTransport(), googleCredential.getHttpTransport(),
googleCredential.getJsonFactory(), googleCredential.getJsonFactory(),
googleCredential) googleCredential.getHttpRequestInitializer())
.setApplicationName(projectId) .setApplicationName(projectId)
.build(); .build();
} }

View file

@ -17,7 +17,7 @@ package google.registry.export.sheet;
import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.Sheets;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.config.CredentialModule.JsonCredential; import google.registry.config.CredentialModule.GoogleWorkspaceCredential;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle; import google.registry.util.GoogleCredentialsBundle;
@ -27,7 +27,7 @@ public final class SheetsServiceModule {
@Provides @Provides
static Sheets provideSheets( static Sheets provideSheets(
@JsonCredential GoogleCredentialsBundle credentialsBundle, @GoogleWorkspaceCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) { @Config("projectId") String projectId) {
return new Sheets.Builder( return new Sheets.Builder(
credentialsBundle.getHttpTransport(), credentialsBundle.getHttpTransport(),

View file

@ -19,7 +19,7 @@ import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings; import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.config.CredentialModule.DefaultCredential; import google.registry.config.CredentialModule.ApplicationDefaultCredential;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle; import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.Retrier; import google.registry.util.Retrier;
@ -33,7 +33,7 @@ public abstract class SecretManagerModule {
@Provides @Provides
@Singleton @Singleton
static SecretManagerServiceSettings provideSecretManagerSetting( static SecretManagerServiceSettings provideSecretManagerSetting(
@DefaultCredential GoogleCredentialsBundle credentialsBundle) { @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle) {
try { try {
return SecretManagerServiceSettings.newBuilder() return SecretManagerServiceSettings.newBuilder()
.setCredentialsProvider(() -> credentialsBundle.getGoogleCredentials()) .setCredentialsProvider(() -> credentialsBundle.getGoogleCredentials())

View file

@ -35,6 +35,7 @@ import dagger.Binds;
import dagger.Lazy; import dagger.Lazy;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
import google.registry.config.CredentialModule.DefaultCredential; import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.CredentialModule.LocalCredential; import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.CredentialModule.LocalCredentialJson; import google.registry.config.CredentialModule.LocalCredentialJson;
@ -228,6 +229,11 @@ public class AuthModule {
@DefaultCredential @DefaultCredential
abstract GoogleCredentialsBundle provideLocalCredentialAsDefaultCredential( abstract GoogleCredentialsBundle provideLocalCredentialAsDefaultCredential(
@LocalCredential GoogleCredentialsBundle credential); @LocalCredential GoogleCredentialsBundle credential);
@Binds
@ApplicationDefaultCredential
abstract GoogleCredentialsBundle provideLocalCredentialAsApplicationDefaultCredential(
@LocalCredential GoogleCredentialsBundle credential);
} }
/** Raised when we need a user login. */ /** Raised when we need a user login. */