mirror of
https://github.com/google/nomulus.git
synced 2025-05-16 17:37:13 +02:00
Add the ability to provide credential JSON file to the nomulus tool
This allows us to run nomulus tool programmatically on environments that do not allow the 3-legged OAuth authentication flow. The provided JSON file corresponds to a service account, which must have GAE admin permission and whose client ID must be whitelisted in the config file. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=226008337
This commit is contained in:
parent
40b05ffb3c
commit
27b6231053
5 changed files with 76 additions and 20 deletions
|
@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
|
import dagger.Lazy;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import google.registry.config.CredentialModule.DefaultCredential;
|
import google.registry.config.CredentialModule.DefaultCredential;
|
||||||
|
@ -45,12 +46,14 @@ import java.io.IOException;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
/**
|
/** Module providing the dependency graph for authorization credentials. */
|
||||||
* Module providing the dependency graph for authorization credentials.
|
|
||||||
*/
|
|
||||||
@Module
|
@Module
|
||||||
public class AuthModule {
|
public class AuthModule {
|
||||||
|
|
||||||
|
@ -84,9 +87,15 @@ public class AuthModule {
|
||||||
@Provides
|
@Provides
|
||||||
@LocalCredential
|
@LocalCredential
|
||||||
public static GoogleCredential provideLocalCredential(
|
public static GoogleCredential provideLocalCredential(
|
||||||
@LocalCredentialJson String credentialJson) {
|
@LocalCredentialJson String credentialJson,
|
||||||
|
@Config("localCredentialOauthScopes") ImmutableList<String> scopes) {
|
||||||
try {
|
try {
|
||||||
return GoogleCredential.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
|
GoogleCredential credential =
|
||||||
|
GoogleCredential.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
|
||||||
|
if (credential.createScopedRequired()) {
|
||||||
|
credential = credential.createScoped(scopes);
|
||||||
|
}
|
||||||
|
return credential;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -133,15 +142,25 @@ public class AuthModule {
|
||||||
@Provides
|
@Provides
|
||||||
@LocalCredentialJson
|
@LocalCredentialJson
|
||||||
public static String provideLocalCredentialJson(
|
public static String provideLocalCredentialJson(
|
||||||
GoogleClientSecrets clientSecrets, @StoredCredential Credential credential) {
|
Lazy<GoogleClientSecrets> clientSecrets,
|
||||||
return new Gson()
|
@StoredCredential Lazy<Credential> credential,
|
||||||
.toJson(
|
@Nullable @Named("credentialFileName") String credentialFilename) {
|
||||||
ImmutableMap.<String, String>builder()
|
try {
|
||||||
.put("type", "authorized_user")
|
if (credentialFilename != null) {
|
||||||
.put("client_id", clientSecrets.getDetails().getClientId())
|
return new String(Files.readAllBytes(Paths.get(credentialFilename)), UTF_8);
|
||||||
.put("client_secret", clientSecrets.getDetails().getClientSecret())
|
} else {
|
||||||
.put("refresh_token", credential.getRefreshToken())
|
return new Gson()
|
||||||
.build());
|
.toJson(
|
||||||
|
ImmutableMap.<String, String>builder()
|
||||||
|
.put("type", "authorized_user")
|
||||||
|
.put("client_id", clientSecrets.get().getDetails().getClientId())
|
||||||
|
.put("client_secret", clientSecrets.get().getDetails().getClientSecret())
|
||||||
|
.put("refresh_token", credential.get().getRefreshToken())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -58,6 +58,13 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
description = "Returns all command names.")
|
description = "Returns all command names.")
|
||||||
private boolean showAllCommands;
|
private boolean showAllCommands;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = {"--credential"},
|
||||||
|
description =
|
||||||
|
"Name of a JSON file containing credential information used by the tool. "
|
||||||
|
+ "If not set, credentials saved by running `nomulus login' will be used.")
|
||||||
|
private String credentialJson = null;
|
||||||
|
|
||||||
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
||||||
@ParametersDelegate
|
@ParametersDelegate
|
||||||
private LoggingParameters loggingParams = new LoggingParameters();
|
private LoggingParameters loggingParams = new LoggingParameters();
|
||||||
|
@ -81,8 +88,6 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
this.commands = commands;
|
this.commands = commands;
|
||||||
|
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
component = DaggerRegistryToolComponent.create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The <? extends Class<? extends Command>> wildcard looks a little funny, but is needed so that
|
// The <? extends Class<? extends Command>> wildcard looks a little funny, but is needed so that
|
||||||
|
@ -146,6 +151,9 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
checkState(RegistryToolEnvironment.get() == environment,
|
checkState(RegistryToolEnvironment.get() == environment,
|
||||||
"RegistryToolEnvironment argument pre-processing kludge failed.");
|
"RegistryToolEnvironment argument pre-processing kludge failed.");
|
||||||
|
|
||||||
|
component =
|
||||||
|
DaggerRegistryToolComponent.builder().credentialFilename(credentialJson).build();
|
||||||
|
|
||||||
// JCommander stores sub-commands as nested JCommander objects containing a list of user objects
|
// JCommander stores sub-commands as nested JCommander objects containing a list of user objects
|
||||||
// to be populated. Extract the subcommand by getting the JCommander wrapper and then
|
// to be populated. Extract the subcommand by getting the JCommander wrapper and then
|
||||||
// retrieving the first (and, by virtue of our usage, only) object from it.
|
// retrieving the first (and, by virtue of our usage, only) object from it.
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
|
import dagger.BindsInstance;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import google.registry.bigquery.BigqueryModule;
|
import google.registry.bigquery.BigqueryModule;
|
||||||
import google.registry.config.CredentialModule.LocalCredentialJson;
|
import google.registry.config.CredentialModule.LocalCredentialJson;
|
||||||
|
@ -36,6 +37,8 @@ import google.registry.util.AppEngineServiceUtilsImpl.AppEngineServiceUtilsModul
|
||||||
import google.registry.util.SystemClock.SystemClockModule;
|
import google.registry.util.SystemClock.SystemClockModule;
|
||||||
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
||||||
import google.registry.whois.WhoisModule;
|
import google.registry.whois.WhoisModule;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,5 +116,12 @@ interface RegistryToolComponent {
|
||||||
|
|
||||||
@LocalCredentialJson
|
@LocalCredentialJson
|
||||||
String googleCredentialJson();
|
String googleCredentialJson();
|
||||||
}
|
|
||||||
|
|
||||||
|
@Component.Builder
|
||||||
|
interface Builder {
|
||||||
|
@BindsInstance
|
||||||
|
Builder credentialFilename(@Nullable @Named("credentialFileName") String credentialFilename);
|
||||||
|
|
||||||
|
RegistryToolComponent build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import com.google.api.client.http.HttpRequestFactory;
|
||||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import google.registry.config.CredentialModule.LocalCredential;
|
import google.registry.config.CredentialModule.DefaultCredential;
|
||||||
import google.registry.config.RegistryConfig;
|
import google.registry.config.RegistryConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,7 @@ class RequestFactoryModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
static HttpRequestFactory provideHttpRequestFactory(
|
static HttpRequestFactory provideHttpRequestFactory(
|
||||||
@LocalCredential GoogleCredential credential) {
|
@DefaultCredential GoogleCredential credential) {
|
||||||
if (RegistryConfig.areServersLocal()) {
|
if (RegistryConfig.areServersLocal()) {
|
||||||
return new NetHttpTransport()
|
return new NetHttpTransport()
|
||||||
.createRequestFactory(
|
.createRequestFactory(
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -32,11 +33,15 @@ import com.google.api.client.util.store.DataStore;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@ -49,6 +54,9 @@ public class AuthModuleTest {
|
||||||
private static final String ACCESS_TOKEN = "FakeAccessToken";
|
private static final String ACCESS_TOKEN = "FakeAccessToken";
|
||||||
private static final String REFRESH_TOKEN = "FakeReFreshToken";
|
private static final String REFRESH_TOKEN = "FakeReFreshToken";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final TemporaryFolder folder = new TemporaryFolder();
|
||||||
|
|
||||||
private final Credential fakeCredential =
|
private final Credential fakeCredential =
|
||||||
new Credential.Builder(
|
new Credential.Builder(
|
||||||
new Credential.AccessMethod() {
|
new Credential.AccessMethod() {
|
||||||
|
@ -154,7 +162,8 @@ public class AuthModuleTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_provideLocalCredentialJson() {
|
public void test_provideLocalCredentialJson() {
|
||||||
String credentialJson = AuthModule.provideLocalCredentialJson(getSecrets(), getCredential());
|
String credentialJson =
|
||||||
|
AuthModule.provideLocalCredentialJson(this::getSecrets, this::getCredential, null);
|
||||||
Map<String, String> jsonMap =
|
Map<String, String> jsonMap =
|
||||||
new Gson().fromJson(credentialJson, new TypeToken<Map<String, String>>() {}.getType());
|
new Gson().fromJson(credentialJson, new TypeToken<Map<String, String>>() {}.getType());
|
||||||
assertThat(jsonMap.get("type")).isEqualTo("authorized_user");
|
assertThat(jsonMap.get("type")).isEqualTo("authorized_user");
|
||||||
|
@ -163,6 +172,16 @@ public class AuthModuleTest {
|
||||||
assertThat(jsonMap.get("refresh_token")).isEqualTo(REFRESH_TOKEN);
|
assertThat(jsonMap.get("refresh_token")).isEqualTo(REFRESH_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_provideExternalCredentialJson() throws Exception {
|
||||||
|
File credentialFile = folder.newFile("credential.json");
|
||||||
|
Files.write(credentialFile.toPath(), "{some_field: some_value}".getBytes(UTF_8));
|
||||||
|
String credentialJson =
|
||||||
|
AuthModule.provideLocalCredentialJson(
|
||||||
|
this::getSecrets, this::getCredential, credentialFile.getCanonicalPath());
|
||||||
|
assertThat(credentialJson).isEqualTo("{some_field: some_value}");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_provideCredential() {
|
public void test_provideCredential() {
|
||||||
Credential cred = getCredential();
|
Credential cred = getCredential();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue