Skip to content
This repository was archived by the owner on Feb 29, 2024. It is now read-only.

Commit b0aff72

Browse files
committed
When using JSON credentials, do not require projectId to be provided as a parameter. Instead, just read it from the JSON file.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152176955
1 parent bd0e597 commit b0aff72

3 files changed

Lines changed: 79 additions & 31 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,9 @@ launcher command (same way as with `-agentpath`):
208208

209209
<pre>
210210
-Dcom.google.cdbg.auth.serviceaccount.enable=<i>true</i>
211-
-Dcom.google.cdbg.auth.serviceaccount.projectid=<i>myprojectid</i>
212-
-Dcom.google.cdbg.auth.serviceaccount.email=<i>email@something.com</i>
213211
-Dcom.google.cdbg.auth.serviceaccount.jsonfile=<i>/opt/cdbg/svc.json</i>
214212
</pre>
215213

216-
You can set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable
214+
You can also set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable
217215
to the JSON file instead of setting the `auth.serviceaccount.jsonfile` argument.
216+

src/agent/internals/src/main/java/com/google/devtools/cdbg/debuglets/java/GcpEnvironment.java

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.google.devtools.cdbg.debuglets.java;
1818

19+
import static com.google.devtools.cdbg.debuglets.java.AgentLogger.warn;
20+
1921
import com.google.devtools.clouddebugger.v2.Labels;
2022

2123
import java.lang.reflect.Constructor;
@@ -87,37 +89,23 @@ static URL getControllerBaseUrl() {
8789
*/
8890
static boolean getIsServiceAccountEnabled() {
8991
String value = getFlag("auth.serviceaccount.enable", "enable_service_account_auth");
90-
return value.equals("1") || value.equalsIgnoreCase("true");
92+
return value.equals("1") || value.equalsIgnoreCase("true");
9193
}
92-
94+
9395
/**
9496
* Lazily creates and returns the class to get access token and project information.
9597
*/
9698
static synchronized MetadataQuery getMetadataQuery() {
9799
// Lazy initialization.
98100
if (metadataQuery == null) {
99101
if (getIsServiceAccountEnabled()) {
100-
String projectId = getFlag("auth.serviceaccount.projectid", "project_id");
101-
if (projectId.isEmpty()) {
102-
throw new RuntimeException(
103-
"Project ID not specified for service account authentication. Please set "
104-
+ "either auth.serviceaccount.projectid system property or project_id agent option");
105-
}
106-
107-
String email = getFlag("auth.serviceaccount.email", "service_account_email");
108-
if (email.isEmpty()) {
109-
throw new RuntimeException(
110-
"Service account e-mail not specified for service account authentication. Please set "
111-
+ "either auth.serviceaccount.email system property or "
112-
+ "service_account_email agent option");
113-
}
114-
115-
String p12File = getFlag("auth.serviceaccount.p12file", "service_account_p12_file");
116102
String jsonFile = environmentStore.get("GOOGLE_APPLICATION_CREDENTIALS");
117103
if (jsonFile == null) {
118104
jsonFile = getFlag("auth.serviceaccount.jsonfile", "service_account_json_file");
119105
}
120106

107+
// TODO(emrekultursay): Remove support for p12 file.
108+
String p12File = getFlag("auth.serviceaccount.p12file", "service_account_p12_file");
121109
if (p12File.isEmpty() && jsonFile.isEmpty()) {
122110
throw new RuntimeException(
123111
"Service account JSON file not specified for service account authentication. "
@@ -126,6 +114,33 @@ static synchronized MetadataQuery getMetadataQuery() {
126114
+ "GOOGLE_APPLICATION_CREDENTIALS environment variable");
127115
}
128116

117+
String projectId;
118+
String email;
119+
120+
if (!p12File.isEmpty()) {
121+
warn("p12 file based service account authentication is deprecated and will be removed. "
122+
+ "Please use JSON file based service account authentication.");
123+
124+
projectId = getFlag("auth.serviceaccount.projectid", "project_id");
125+
if (projectId.isEmpty()) {
126+
throw new RuntimeException(
127+
"Project ID not specified for service account authentication. Please set either"
128+
+ "auth.serviceaccount.projectid system property or project_id agent option");
129+
}
130+
131+
email = getFlag("auth.serviceaccount.email", "service_account_email");
132+
if (email.isEmpty()) {
133+
throw new RuntimeException(
134+
"Service account e-mail not specified for service account authentication. Please "
135+
+ "set either auth.serviceaccount.email system property or "
136+
+ "service_account_email agent option");
137+
}
138+
} else {
139+
// These will be read from the JSON file.
140+
projectId = "";
141+
email = "";
142+
}
143+
129144
try {
130145
// Use reflection to create a new instance of "ServiceAccountAuth" class. This way this
131146
// class has no explicit dependency on "com.google.api.client" package that can be

src/agent/internals/src/main/java/com/google/devtools/cdbg/debuglets/java/ServiceAccountAuth.java

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
2020
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
21+
import com.google.api.client.json.JsonParser;
2122
import com.google.api.client.json.jackson2.JacksonFactory;
23+
import com.google.api.client.util.Key;
2224

2325
import java.io.File;
2426
import java.io.FileInputStream;
@@ -32,27 +34,39 @@
3234
* Exchanges service account private key for OAuth access token.
3335
*/
3436
final class ServiceAccountAuth implements MetadataQuery {
37+
38+
/** The JSON Service Account file gets parsed into an object of this type. */
39+
public static class ServiceAccountAuthJsonFile {
40+
/** The only field we are interested in is project_id, the rest will be ignored. */
41+
@Key("project_id")
42+
private String projectId = null;
43+
44+
String getProjectId() {
45+
return projectId;
46+
}
47+
}
48+
3549
/**
3650
* OAuth scope requested.
3751
*/
3852
private static final String CLOUD_PLATFORM_SCOPE =
3953
"https://www.googleapis.com/auth/cloud-platform";
40-
54+
4155
/**
42-
* Time to refresh the token before it expires.
56+
* Time to refresh the token before it expires.
4357
*/
4458
private static final int TOKEN_EXPIRATION_BUFFER_SEC = 60;
4559

4660
/**
4761
* GCP project ID that created the service account.
4862
*/
4963
private final String projectId;
50-
64+
5165
/**
5266
* GCP project number corresponding to projectId.
5367
*/
5468
private final String projectNumber;
55-
69+
5670
/**
5771
* Runs OAuth flow to obtain the access token.
5872
*/
@@ -79,10 +93,10 @@ public ServiceAccountAuth(
7993
Objects.requireNonNull(serviceAccountP12File);
8094
Objects.requireNonNull(serviceAccountJsonFile);
8195

82-
this.projectId = projectId;
83-
this.projectNumber = projectNumber;
84-
8596
if (!serviceAccountP12File.isEmpty()) {
97+
this.projectId = projectId;
98+
this.projectNumber = projectNumber;
99+
86100
this.credential = new GoogleCredential.Builder()
87101
.setTransport(GoogleNetHttpTransport.newTrustedTransport())
88102
.setJsonFactory(JacksonFactory.getDefaultInstance())
@@ -91,7 +105,10 @@ public ServiceAccountAuth(
91105
.setServiceAccountScopes(Collections.singleton(CLOUD_PLATFORM_SCOPE))
92106
.build();
93107
} else if (!serviceAccountJsonFile.isEmpty()) {
94-
InputStream serviceAccountJsonStream = new FileInputStream(new File(serviceAccountJsonFile));
108+
this.projectId = parseServiceAccountAuthJsonFile(serviceAccountJsonFile).getProjectId();
109+
this.projectNumber = this.projectId;
110+
111+
InputStream serviceAccountJsonStream = new FileInputStream(serviceAccountJsonFile);
95112
this.credential = GoogleCredential.fromStream(serviceAccountJsonStream)
96113
.createScoped(Collections.singleton(CLOUD_PLATFORM_SCOPE));
97114
} else {
@@ -109,10 +126,10 @@ public String getProjectId() {
109126
public String getProjectNumber() {
110127
return projectNumber;
111128
}
112-
129+
113130
/**
114131
* Gets the cached OAuth access token refreshing it as needed.
115-
*
132+
*
116133
* <p> Access token refresh is done synchronously delaying the call.
117134
*/
118135
@Override
@@ -135,4 +152,21 @@ public synchronized String getAccessToken() {
135152
public void shutdown() {
136153
// TODO(vlif): implement if not handled properly by JVM shutdown.
137154
}
155+
156+
157+
/** Parses the given JSON auth file.
158+
* <p> TODO(emrekultursay): Remove this method when a new version of the google-api-java-client
159+
* library is released (newer than v22), and replace it with the new
160+
* GoogleCredential.getServiceAccountProjectId() method. */
161+
private static ServiceAccountAuthJsonFile parseServiceAccountAuthJsonFile(
162+
String serviceAccountJsonFile) throws IOException {
163+
JsonParser parser = JacksonFactory.getDefaultInstance().createJsonParser(
164+
new FileInputStream(serviceAccountJsonFile));
165+
ServiceAccountAuthJsonFile parsedFile = parser.parse(ServiceAccountAuthJsonFile.class);
166+
if (parsedFile.getProjectId() == null) {
167+
throw new IllegalArgumentException("Service account JSON file is missing project_id field");
168+
}
169+
170+
return parsedFile;
171+
}
138172
}

0 commit comments

Comments
 (0)