From ae2d8db82e2e05f9c1e7e174611e4152c1bff27f Mon Sep 17 00:00:00 2001 From: Dan Przybylowski Date: Fri, 25 May 2018 15:43:24 +0100 Subject: [PATCH] #1007 - adding option to impersonate a user with backward compatibility --- .../auth/oauth2/GoogleCredential.java | 16 +- .../oauth2/DefaultCredentialProviderTest.java | 148 ++++++++++-------- 2 files changed, 98 insertions(+), 66 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java index b95f43705..e68c62940 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java @@ -497,6 +497,17 @@ public GoogleCredential createScoped(Collection scopes) { .build(); } + /** + * {@link Beta}
+ * For credentials that require scopes, creates a copy of the credential with the specified + * scopes for specified user. + */ + @Beta + public GoogleCredential createScoped(Collection scopes, String serviceAccountUser) { + this.serviceAccountUser = serviceAccountUser; + return createScoped(scopes); + } + /** * Google credential builder. * @@ -862,11 +873,10 @@ private static PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOExc } byte[] bytes = section.getBase64DecodedBytes(); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); - Exception unexpectedException = null; + Exception unexpectedException; try { KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory(); - PrivateKey privateKey = keyFactory.generatePrivate(keySpec); - return privateKey; + return keyFactory.generatePrivate(keySpec); } catch (NoSuchAlgorithmException exception) { unexpectedException = exception; } catch (InvalidKeySpecException exception) { diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java index e3ff493f6..c420ae72f 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java @@ -26,6 +26,7 @@ import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpRequest; +import junit.framework.TestCase; import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -40,7 +41,6 @@ import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import junit.framework.TestCase; /** * Tests {@link DefaultCredentialProvider}. @@ -74,6 +74,11 @@ public class DefaultCredentialProviderTest extends TestCase { private static File tempDirectory = null; + private static final String SERVICE_ACCOUNT_ID = + "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr.apps.googleusercontent.com"; + private static final String SERVICE_ACCOUNT_EMAIL = + "36680232662-vrd7ji19qe3nelgchdcsanun6bnr@developer.gserviceaccount.com"; + public void testDefaultCredentialAppEngineDeployed() throws IOException { HttpTransport transport = new MockHttpTransport(); TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); @@ -288,31 +293,14 @@ public void testDefaultCredentialNotFoundError() { public void testDefaultCredentialServiceAccount() throws IOException { File serviceAccountFile = new java.io.File(getTempDirectory(), "DefaultCredentialServiceAccount.json"); - if (serviceAccountFile.exists()) { - serviceAccountFile.delete(); - } - final String serviceAccountId = - "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr.apps.googleusercontent.com"; - final String serviceAccountEmail = - "36680232662-vrd7ji19qe3nelgchdcsanun6bnr@developer.gserviceaccount.com"; + deleteFile(serviceAccountFile); MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount(serviceAccountEmail, ACCESS_TOKEN); + transport.addServiceAccount(SERVICE_ACCOUNT_EMAIL, ACCESS_TOKEN); TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); try { - // Write out service account file - GenericJson serviceAccountContents = new GenericJson(); - serviceAccountContents.setFactory(JSON_FACTORY); - serviceAccountContents.put("client_id", serviceAccountId); - serviceAccountContents.put("client_email", serviceAccountEmail); - serviceAccountContents.put("private_key", SA_KEY_TEXT); - serviceAccountContents.put("private_key_id", SA_KEY_ID); - serviceAccountContents.put("type", GoogleCredential.SERVICE_ACCOUNT_FILE_TYPE); - PrintWriter writer = new PrintWriter(serviceAccountFile); - String json = serviceAccountContents.toPrettyString(); - writer.println(json); - writer.close(); + writeOutServiceAccountFile(serviceAccountFile); // Point the default credential to the file testProvider.setEnv(DefaultCredentialProvider.CREDENTIAL_ENV_VAR, @@ -325,17 +313,40 @@ public void testDefaultCredentialServiceAccount() throws IOException { assertTrue(credential.refreshToken()); assertEquals(ACCESS_TOKEN, credential.getAccessToken()); } finally { - if (serviceAccountFile.exists()) { - serviceAccountFile.delete(); - } + deleteFile(serviceAccountFile); + } + } + + public void testDefaultCredentialServiceAccountWithCustomServiceAccountUser() throws IOException { + File serviceAccountFile = new java.io.File(getTempDirectory(), + "DefaultCredentialServiceAccount.json"); + deleteFile(serviceAccountFile); + + MockTokenServerTransport transport = new MockTokenServerTransport(); + transport.addServiceAccount(SERVICE_ACCOUNT_EMAIL, ACCESS_TOKEN); + + TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); + try { + writeOutServiceAccountFile(serviceAccountFile); + + // Point the default credential to the file + testProvider.setEnv(DefaultCredentialProvider.CREDENTIAL_ENV_VAR, + serviceAccountFile.getAbsolutePath()); + + GoogleCredential credential = testProvider.getDefaultCredential(transport, JSON_FACTORY); + assertNotNull(credential); + credential = credential.createScoped(SCOPES, "google@google.com"); + + assertTrue(credential.refreshToken()); + assertEquals(ACCESS_TOKEN, credential.getAccessToken()); + } finally { + deleteFile(serviceAccountFile); } } public void testDefaultCredentialUser() throws IOException { File userCredentialFile = new java.io.File(getTempDirectory(), "DefaultCredentialUser.json"); - if (userCredentialFile.exists()) { - userCredentialFile.delete(); - } + deleteFile(userCredentialFile); TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); // Point the default credential to the file @@ -349,18 +360,15 @@ public void testDefaultCredentialWellKnownFileNonWindows() throws IOException { // Simulate where the SDK puts the well-known file on non-Windows platforms File homeDir = getTempDirectory(); File configDir = new File(homeDir, ".config"); - if (!configDir.exists()) { - configDir.mkdir(); - } + mkdir(configDir); + File cloudConfigDir = new File(configDir, DefaultCredentialProvider.CLOUDSDK_CONFIG_DIRECTORY); - if (!cloudConfigDir.exists()) { - cloudConfigDir.mkdir(); - } + mkdir(cloudConfigDir); + File wellKnownFile = new File( cloudConfigDir, DefaultCredentialProvider.WELL_KNOWN_CREDENTIALS_FILE); - if (wellKnownFile.exists()) { - wellKnownFile.delete(); - } + deleteFile(wellKnownFile); + TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); testProvider.addFile(wellKnownFile.getAbsolutePath()); testProvider.setProperty("os.name", "linux"); @@ -373,14 +381,12 @@ public void testDefaultCredentialWellKnownFileWindows() throws IOException { // Simulate where the SDK puts the well-known file on Windows File appDataDir = getTempDirectory(); File cloudConfigDir = new File(appDataDir, DefaultCredentialProvider.CLOUDSDK_CONFIG_DIRECTORY); - if (!cloudConfigDir.exists()) { - cloudConfigDir.mkdir(); - } + mkdir(cloudConfigDir); + File wellKnownFile = new File( cloudConfigDir, DefaultCredentialProvider.WELL_KNOWN_CREDENTIALS_FILE); - if (wellKnownFile.exists()) { - wellKnownFile.delete(); - } + deleteFile(wellKnownFile); + TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider(); testProvider.addFile(wellKnownFile.getAbsolutePath()); testProvider.setProperty("os.name", "windows"); @@ -401,27 +407,23 @@ public void testDefaultCredentialEnvironmentVariableWinsOverWellKnownFile() thro // Set up an environment variable file File environmentVariableFile = new java.io.File(getTempDirectory(), "EnvVarUser.json"); - if (environmentVariableFile.exists()) { - environmentVariableFile.delete(); - } + deleteFile(environmentVariableFile); + testProvider.setEnv(DefaultCredentialProvider.CREDENTIAL_ENV_VAR, environmentVariableFile.getAbsolutePath()); // Also set up a well-known-location file File homeDir = getTempDirectory(); File configDir = new File(homeDir, ".config"); - if (!configDir.exists()) { - configDir.mkdir(); - } + mkdir(configDir); + File cloudConfigDir = new File(configDir, DefaultCredentialProvider.CLOUDSDK_CONFIG_DIRECTORY); - if (!cloudConfigDir.exists()) { - cloudConfigDir.mkdir(); - } + mkdir(cloudConfigDir); + File wellKnownFile = new File( cloudConfigDir, DefaultCredentialProvider.WELL_KNOWN_CREDENTIALS_FILE); - if (wellKnownFile.exists()) { - wellKnownFile.delete(); - } + deleteFile(wellKnownFile); + testProvider.addFile(wellKnownFile.getAbsolutePath()); testProvider.setProperty("os.name", "linux"); testProvider.setProperty("user.home", homeDir.getAbsolutePath()); @@ -451,12 +453,8 @@ public void testDefaultCredentialEnvironmentVariableWinsOverWellKnownFile() thro assertTrue(credential.refreshToken()); assertEquals(accessTokenEnv, credential.getAccessToken()); } finally { - if (wellKnownFile.exists()) { - wellKnownFile.delete(); - } - if (environmentVariableFile.exists()) { - environmentVariableFile.delete(); - } + deleteFile(wellKnownFile); + deleteFile(environmentVariableFile); } } @@ -487,9 +485,7 @@ private void testDefaultCredentialUser(File userFile, TestDefaultCredentialProvi assertTrue(credential.refreshToken()); assertEquals(ACCESS_TOKEN, credential.getAccessToken()); } finally { - if (userFile.exists()) { - userFile.delete(); - } + deleteFile(userFile); } } @@ -512,6 +508,32 @@ private static File getTempDirectory() { return tempDirectory; } + private void writeOutServiceAccountFile(File serviceAccountFile) throws IOException { + GenericJson serviceAccountContents = new GenericJson(); + serviceAccountContents.setFactory(JSON_FACTORY); + serviceAccountContents.put("client_id", SERVICE_ACCOUNT_ID); + serviceAccountContents.put("client_email", SERVICE_ACCOUNT_EMAIL); + serviceAccountContents.put("private_key", SA_KEY_TEXT); + serviceAccountContents.put("private_key_id", SA_KEY_ID); + serviceAccountContents.put("type", GoogleCredential.SERVICE_ACCOUNT_FILE_TYPE); + PrintWriter writer = new PrintWriter(serviceAccountFile); + String json = serviceAccountContents.toPrettyString(); + writer.println(json); + writer.close(); + } + + private void deleteFile(File file) { + if (file.exists()) { + file.delete(); + } + } + + private void mkdir(File dir) { + if (!dir.exists()) { + dir.mkdir(); + } + } + public static class MockAppEngineCredential extends GoogleCredential { public MockAppEngineCredential(HttpTransport transport, JsonFactory jsonFactory) { super(new GoogleCredential.Builder().setTransport(transport).setJsonFactory(jsonFactory));