19
19
import com .google .common .annotations .VisibleForTesting ;
20
20
import com .google .common .base .Preconditions ;
21
21
import com .google .common .base .Strings ;
22
+ import com .google .devtools .build .lib .events .Event ;
23
+ import com .google .devtools .build .lib .events .Reporter ;
24
+ import com .google .devtools .build .lib .vfs .FileSystem ;
25
+ import com .google .devtools .build .lib .vfs .Path ;
22
26
import io .grpc .CallCredentials ;
23
27
import io .grpc .ClientInterceptor ;
24
28
import io .grpc .ManagedChannel ;
41
45
import java .io .IOException ;
42
46
import java .io .InputStream ;
43
47
import java .util .List ;
48
+ import java .util .Map ;
49
+ import java .util .Optional ;
44
50
import java .util .concurrent .Executor ;
45
51
import java .util .concurrent .TimeUnit ;
46
52
import javax .annotation .Nullable ;
@@ -186,14 +192,17 @@ private static NettyChannelBuilder newNettyChannelBuilder(String targetUrl, Stri
186
192
}
187
193
188
194
/**
189
- * Create a new {@link CallCredentials} object.
195
+ * Create a new {@link CallCredentials} object from the authentication flags, or null if no flags
196
+ * are set.
190
197
*
191
- * @throws IOException in case the call credentials can't be constructed.
198
+ * @throws IOException in case the credentials can't be constructed.
192
199
*/
193
- public static CallCredentials newCallCredentials (AuthAndTLSOptions options ) throws IOException {
194
- Credentials creds = newCredentials (options );
195
- if (creds != null ) {
196
- return MoreCallCredentials .from (creds );
200
+ @ Nullable
201
+ public static CallCredentials newGoogleCallCredentials (AuthAndTLSOptions options )
202
+ throws IOException {
203
+ Optional <Credentials > creds = newGoogleCredentials (options );
204
+ if (creds .isPresent ()) {
205
+ return MoreCallCredentials .from (creds .get ());
197
206
}
198
207
return null ;
199
208
}
@@ -210,18 +219,52 @@ public static CallCredentialsProvider newCallCredentialsProvider(@Nullable Crede
210
219
}
211
220
212
221
/**
213
- * Create a new {@link Credentials} object, or {@code null} if no options are provided.
222
+ * Create a new {@link Credentials} with following order:
223
+ *
224
+ * <ol>
225
+ * <li>If authentication enabled by flags, use it to create credentials
226
+ * <li>Use .netrc to provide credentials if exists
227
+ * <li>Otherwise, return {@code null}
228
+ * </ol>
214
229
*
215
230
* @throws IOException in case the credentials can't be constructed.
216
231
*/
217
232
@ Nullable
218
- public static Credentials newCredentials (@ Nullable AuthAndTLSOptions options ) throws IOException {
233
+ public static Credentials newCredentials (
234
+ Reporter reporter ,
235
+ Map <String , String > clientEnv ,
236
+ FileSystem fileSystem ,
237
+ AuthAndTLSOptions authAndTlsOptions )
238
+ throws IOException {
239
+ Optional <Credentials > credentials = newGoogleCredentials (authAndTlsOptions );
240
+
241
+ if (credentials .isEmpty ()) {
242
+ // Fallback to .netrc if it exists.
243
+ try {
244
+ credentials = newCredentialsFromNetrc (clientEnv , fileSystem );
245
+ } catch (IOException e ) {
246
+ // TODO(yannic): Make this fail the build.
247
+ reporter .handle (Event .warn (e .getMessage ()));
248
+ }
249
+ }
250
+
251
+ return credentials .orElse (null );
252
+ }
253
+
254
+ /**
255
+ * Create a new {@link Credentials} object from the authentication flags, or null if no flags are
256
+ * set.
257
+ *
258
+ * @throws IOException in case the credentials can't be constructed.
259
+ */
260
+ public static Optional <Credentials > newGoogleCredentials (@ Nullable AuthAndTLSOptions options )
261
+ throws IOException {
219
262
if (options == null ) {
220
- return null ;
263
+ return Optional . empty () ;
221
264
} else if (options .googleCredentials != null ) {
222
265
// Credentials from file
223
266
try (InputStream authFile = new FileInputStream (options .googleCredentials )) {
224
- return newCredentials ( authFile , options .googleAuthScopes );
267
+ return Optional . of ( newGoogleCredentialsFromFile ( authFile , options .googleAuthScopes ) );
225
268
} catch (FileNotFoundException e ) {
226
269
String message =
227
270
String .format (
@@ -230,10 +273,11 @@ public static Credentials newCredentials(@Nullable AuthAndTLSOptions options) th
230
273
throw new IOException (message , e );
231
274
}
232
275
} else if (options .useGoogleDefaultCredentials ) {
233
- return newCredentials (
234
- null /* Google Application Default Credentials */ , options .googleAuthScopes );
276
+ return Optional .of (
277
+ newGoogleCredentialsFromFile (
278
+ null /* Google Application Default Credentials */ , options .googleAuthScopes ));
235
279
}
236
- return null ;
280
+ return Optional . empty () ;
237
281
}
238
282
239
283
/**
@@ -242,7 +286,7 @@ public static Credentials newCredentials(@Nullable AuthAndTLSOptions options) th
242
286
* @throws IOException in case the credentials can't be constructed.
243
287
*/
244
288
@ VisibleForTesting
245
- public static Credentials newCredentials (
289
+ public static Credentials newGoogleCredentialsFromFile (
246
290
@ Nullable InputStream credentialsFile , List <String > authScopes ) throws IOException {
247
291
try {
248
292
GoogleCredentials creds =
@@ -258,4 +302,40 @@ public static Credentials newCredentials(
258
302
throw new IOException (message , e );
259
303
}
260
304
}
305
+
306
+ /**
307
+ * Create a new {@link Credentials} object by parsing the .netrc file with following order to
308
+ * search it:
309
+ *
310
+ * <ol>
311
+ * <li>If environment variable $NETRC exists, use it as the path to the .netrc file
312
+ * <li>Fallback to $HOME/.netrc
313
+ * </ol>
314
+ *
315
+ * @return the {@link Credentials} object or {@code null} if there is no .netrc file.
316
+ * @throws IOException in case the credentials can't be constructed.
317
+ */
318
+ @ VisibleForTesting
319
+ static Optional <Credentials > newCredentialsFromNetrc (
320
+ Map <String , String > clientEnv , FileSystem fileSystem ) throws IOException {
321
+ Optional <String > netrcFileString =
322
+ Optional .ofNullable (clientEnv .get ("NETRC" ))
323
+ .or (() -> Optional .ofNullable (clientEnv .get ("HOME" )).map (home -> home + "/.netrc" ));
324
+ if (netrcFileString .isEmpty ()) {
325
+ return Optional .empty ();
326
+ }
327
+
328
+ Path netrcFile = fileSystem .getPath (netrcFileString .get ());
329
+ if (!netrcFile .exists ()) {
330
+ return Optional .empty ();
331
+ }
332
+
333
+ try {
334
+ Netrc netrc = NetrcParser .parseAndClose (netrcFile .getInputStream ());
335
+ return Optional .of (new NetrcCredentials (netrc ));
336
+ } catch (IOException e ) {
337
+ throw new IOException (
338
+ "Failed to parse " + netrcFile .getPathString () + ": " + e .getMessage (), e );
339
+ }
340
+ }
261
341
}
0 commit comments