google-nomulus/java/google/registry/tools/AuthModule.java
jianglai a612e9bf66 Use local credential to deploy beam pipelines
We are moving away from using Application Default Credentials generated by "gcloud auth application-default login" in our code base and consolidate on using self-managed credentials provided from AuthModule.

One of the remaining dependencies on the ADCs is from beam pipeline deployment commands, which by default use the ADCs to talk to GCS and upload the jar files and templates. In this CL, we explicitly provide the locally created credential to the Options used in deployments.

Also moved all credential qualifiers to CredentialModule, and removed @AppEngineAdminApiCredential, which is no longer used.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=224199812
2018-12-05 16:07:54 -05:00

200 lines
7 KiB
Java

// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.AbstractDataStoreFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Ordering;
import com.google.gson.Gson;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.CredentialModule.LocalCredentialJson;
import google.registry.config.RegistryConfig.Config;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
import javax.inject.Singleton;
/**
* Module providing the dependency graph for authorization credentials.
*/
@Module
public class AuthModule {
private static final File DATA_STORE_DIR =
new File(System.getProperty("user.home"), ".config/nomulus/credentials");
@Module
abstract static class LocalCredentialModule {
@Binds
@DefaultCredential
abstract GoogleCredential provideLocalCredentialAsDefaultCredential(
@LocalCredential GoogleCredential credential);
}
@Provides
@StoredCredential
static Credential provideCredential(
GoogleAuthorizationCodeFlow flow, @ClientScopeQualifier String clientScopeQualifier) {
try {
// Try to load the credentials, throw an exception if we fail.
Credential credential = flow.loadCredential(clientScopeQualifier);
if (credential == null) {
throw new LoginRequiredException();
}
return credential;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Provides
@LocalCredential
public static GoogleCredential provideLocalCredential(
@LocalCredentialJson String credentialJson) {
try {
return GoogleCredential.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Provides
public static GoogleAuthorizationCodeFlow provideAuthorizationCodeFlow(
JsonFactory jsonFactory,
GoogleClientSecrets clientSecrets,
@Config("localCredentialOauthScopes") ImmutableList<String> requiredOauthScopes,
AbstractDataStoreFactory dataStoreFactory) {
try {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), jsonFactory, clientSecrets, requiredOauthScopes)
.setDataStoreFactory(dataStoreFactory)
.build();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Provides
public static AuthorizationCodeInstalledApp provideAuthorizationCodeInstalledApp(
GoogleAuthorizationCodeFlow flow) {
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver());
}
@Provides
static Details provideDefaultInstalledDetails() {
return new Details()
.setAuthUri("https://accounts.google.com/o/oauth2/auth")
.setTokenUri("https://accounts.google.com/o/oauth2/token")
.setRedirectUris(ImmutableList.of("urn:ietf:wg:oauth:2.0:oob", "http://localhost"));
}
@Provides
public static GoogleClientSecrets provideClientSecrets(
@Config("toolsClientId") String clientId,
@Config("toolsClientSecret") String clientSecret,
Details details) {
return new GoogleClientSecrets()
.setInstalled(details.setClientId(clientId).setClientSecret(clientSecret));
}
@Provides
@LocalCredentialJson
public static String provideLocalCredentialJson(
GoogleClientSecrets clientSecrets, @StoredCredential Credential credential) {
return new Gson()
.toJson(
ImmutableMap.<String, String>builder()
.put("type", "authorized_user")
.put("client_id", clientSecrets.getDetails().getClientId())
.put("client_secret", clientSecrets.getDetails().getClientSecret())
.put("refresh_token", credential.getRefreshToken())
.build());
}
@Provides
@OAuthClientId
static String provideClientId(GoogleClientSecrets clientSecrets) {
return clientSecrets.getDetails().getClientId();
}
@Provides
@ClientScopeQualifier
static String provideClientScopeQualifier(
@OAuthClientId String clientId,
@Config("localCredentialOauthScopes") ImmutableList<String> scopes) {
return clientId + " " + Joiner.on(" ").join(Ordering.natural().sortedCopy(scopes));
}
@Provides
@Singleton
public static AbstractDataStoreFactory provideDataStoreFactory() {
try {
return new FileDataStoreFactory(DATA_STORE_DIR);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
/** Raised when we need a user login. */
static class LoginRequiredException extends RuntimeException {
LoginRequiredException() {}
}
/**
* Dagger qualifier for the {@link Credential} constructed from the data stored on disk.
*
* <p>This {@link Credential} should not be used in another module, hence the private qualifier.
* It's only use is to build a {@link GoogleCredential}, which is used in injection sites
* elsewhere.
*/
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
private @interface StoredCredential {}
/** Dagger qualifier for the credential qualifier consisting of client and scopes. */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface ClientScopeQualifier {}
/** Dagger qualifier for the OAuth2 client id. */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface OAuthClientId {}
}