Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 156 additions & 33 deletions oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,19 @@
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.json.webtoken.JsonWebToken;
import com.google.api.client.util.Base64;
import com.google.api.client.util.Clock;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.SecurityUtils;
import com.google.api.client.util.StringUtils;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
Expand All @@ -56,6 +61,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
Expand All @@ -65,16 +71,25 @@
import java.util.Objects;

public class GdchCredentials extends GoogleCredentials {
static final String SUPPORTED_FORMAT_VERSION = "1";
private static String VALUE_NOT_FOUND_MESSAGE = "%sExpected value %s not found.";

Check warning on line 74 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "VALUE_NOT_FOUND_MESSAGE" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE4&open=AZy69q6Pgw7BYu5XViE4&pullRequest=1896
private static String VALUE_WRONG_TYPE_MESSAGE = "%sExpected %s value %s of wrong type.";

Check warning on line 75 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this field "VALUE_WRONG_TYPE_MESSAGE" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE5&open=AZy69q6Pgw7BYu5XViE5&pullRequest=1896
private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. ";
@VisibleForTesting static final String SUPPORTED_FORMAT_VERSION = "1";

private static final String ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
private static final String SERVICE_ACCOUNT_TOKEN_TYPE =
"urn:k8s:params:oauth:token-type:serviceaccount";
private static final String TOKEN_TYPE_TOKEN_EXCHANGE =
"urn:ietf:params:oauth:token-type:token-exchange";

private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600;

private final PrivateKey privateKey;
private final String privateKeyId;
private final String projectId;
private final String serviceIdentityName;
private final URI tokenServerUri;
private final URI apiAudience;
private final String apiAudience;
private final int lifetime;
private final String transportFactoryClassName;
private final String caCertPath;
Expand Down Expand Up @@ -218,7 +233,7 @@
*/
static GdchCredentials fromPkcs8(String privateKeyPkcs8, GdchCredentials.Builder builder)
throws IOException {
PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(privateKeyPkcs8);
PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(privateKeyPkcs8, "EC");
builder.setPrivateKey(privateKey);

return new GdchCredentials(builder);
Expand All @@ -229,7 +244,7 @@
*
* @param apiAudience The intended audience for GDCH credentials.
*/
public GdchCredentials createWithGdchAudience(URI apiAudience) throws IOException {
public GdchCredentials createWithGdchAudience(String apiAudience) throws IOException {

Check warning on line 247 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE6&open=AZy69q6Pgw7BYu5XViE6&pullRequest=1896
Preconditions.checkNotNull(
apiAudience, "Audience are not configured for GDCH service account credentials.");
return this.toBuilder().setGdchAudience(apiAudience).build();
Expand All @@ -249,14 +264,19 @@
"Audience are not configured for GDCH service account. Specify the "
+ "audience by calling createWithGDCHAudience.");

JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY;
long currentTime = clock.currentTimeMillis();
String assertion = createAssertion(jsonFactory, currentTime, getApiAudience());
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();

long currentTime = Clock.SYSTEM.currentTimeMillis();
String assertion = createAssertion(jsonFactory, currentTime, apiAudience);

GenericData tokenRequest = new GenericData();
tokenRequest.set("grant_type", OAuth2Utils.TOKEN_TYPE_TOKEN_EXCHANGE);
tokenRequest.set("assertion", assertion);
UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
tokenRequest.set("audience", apiAudience);
tokenRequest.set("grant_type", TOKEN_TYPE_TOKEN_EXCHANGE);
tokenRequest.set("requested_token_type", ACCESS_TOKEN_TYPE);
tokenRequest.set("subject_token", assertion);
tokenRequest.set("subject_token_type", SERVICE_ACCOUNT_TOKEN_TYPE);

JsonHttpContent content = new JsonHttpContent(jsonFactory, tokenRequest);

HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory();
HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(tokenServerUri), content);
Expand All @@ -270,18 +290,16 @@
response = request.execute();
} catch (HttpResponseException re) {
String message = String.format(errorTemplate, re.getMessage(), getServiceIdentityName());
throw GoogleAuthException.createWithTokenEndpointResponseException(re, message);
throw new IOException(message, re);
} catch (IOException e) {
throw GoogleAuthException.createWithTokenEndpointIOException(
e, String.format(errorTemplate, e.getMessage(), getServiceIdentityName()));
String message = String.format(errorTemplate, e.getMessage(), getServiceIdentityName());
throw new IOException(message, e);
}

GenericData responseData = response.parseAs(GenericData.class);
String accessToken =
OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX);
int expiresInSeconds =
OAuth2Utils.validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX);
String accessToken = validateString(responseData, "access_token", PARSE_ERROR_PREFIX);
int expiresInSeconds = validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX);
long expiresAtMilliseconds = clock.currentTimeMillis() + expiresInSeconds * 1000L;

Check failure on line 302 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this usage of "clock", it is annotated with @VisibleForTesting and should not be accessed from production code.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE7&open=AZy69q6Pgw7BYu5XViE7&pullRequest=1896
return new AccessToken(accessToken, new Date(expiresAtMilliseconds));
}

Expand All @@ -292,10 +310,10 @@
* (tokenServerUri), not for API call. It uses the serviceIdentityName as the `iss` and `sub`
* claim, and the tokenServerUri as the `aud` claim. The JWT is signed with the privateKey.
*/
String createAssertion(JsonFactory jsonFactory, long currentTime, URI apiAudience)
String createAssertion(JsonFactory jsonFactory, long currentTime, String apiAudience)

Check warning on line 313 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused method parameter "apiAudience".

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE8&open=AZy69q6Pgw7BYu5XViE8&pullRequest=1896
throws IOException {
JsonWebSignature.Header header = new JsonWebSignature.Header();
header.setAlgorithm("RS256");
header.setAlgorithm("ES256");
header.setType("JWT");
header.setKeyId(privateKeyId);

Expand All @@ -304,12 +322,11 @@
payload.setSubject(getIssuerSubjectValue(projectId, serviceIdentityName));
payload.setIssuedAtTimeSeconds(currentTime / 1000);
payload.setExpirationTimeSeconds(currentTime / 1000 + this.lifetime);
payload.setAudience(getTokenServerUri().toString());
payload.setAudience(tokenServerUri.toString());

String assertion;
try {
payload.set("api_audience", apiAudience.toString());
assertion = JsonWebSignature.signUsingRsaSha256(privateKey, jsonFactory, header, payload);
assertion = signUsingEsSha256(privateKey, jsonFactory, header, payload);
} catch (GeneralSecurityException e) {
throw new IOException(
"Error signing service account access token request with private key.", e);
Expand All @@ -329,11 +346,7 @@
return String.format("system:serviceaccount:%s:%s", projectId, serviceIdentityName);
}

/**
* @return the projectId set in the GDCH SA Key file or the user set projectId
*/
@Override
public final String getProjectId() {

Check warning on line 349 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the "@Override" annotation above this method signature

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE9&open=AZy69q6Pgw7BYu5XViE9&pullRequest=1896
return projectId;
}

Expand All @@ -353,7 +366,7 @@
return tokenServerUri;
}

public final URI getApiAudience() {
public final String getApiAudience() {
return apiAudience;
}

Expand Down Expand Up @@ -436,7 +449,7 @@
private PrivateKey privateKey;
private String serviceIdentityName;
private URI tokenServerUri;
private URI apiAudience;
private String apiAudience;
private HttpTransportFactory transportFactory;
private String caCertPath;
private int lifetime = DEFAULT_LIFETIME_IN_SECONDS;
Expand Down Expand Up @@ -497,7 +510,7 @@
}

@CanIgnoreReturnValue
public Builder setGdchAudience(URI apiAudience) {
public Builder setGdchAudience(String apiAudience) {
this.apiAudience = apiAudience;
return this;
}
Expand Down Expand Up @@ -553,13 +566,16 @@
/*
* Internal HttpTransportFactory for GDCH credentials.
*
* <p> GDCH authentication server could use a self-signed certificate, thus the client could
* <p> GDCH authentication server could use a self-signed certificate, thus the
* client could
* provide the CA certificate path through the `ca_cert_path` in GDCH JSON file.
*
* <p> The TransportFactoryForGdch subclass would read the certificate and create a trust store,
* <p> The TransportFactoryForGdch subclass would read the certificate and
* create a trust store,
* then use the trust store to create a transport.
*
* <p> If the GDCH authentication server uses well known CA certificate, then a regular transport
* <p> If the GDCH authentication server uses well known CA certificate, then a
* regular transport
* would be set.
*/
static class TransportFactoryForGdch implements HttpTransportFactory {
Expand Down Expand Up @@ -594,4 +610,111 @@
}
}
}

/** Return the specified string from JSON or throw a helpful error message. */
private static String validateString(Map<String, Object> map, String key, String errorPrefix)
throws IOException {
Object value = map.get(key);
if (value == null) {
throw new IOException(String.format(VALUE_NOT_FOUND_MESSAGE, errorPrefix, key));
}
if (!(value instanceof String)) {
throw new IOException(String.format(VALUE_WRONG_TYPE_MESSAGE, errorPrefix, "string", key));
}
return (String) value;
}

private static int validateInt32(Map<String, Object> map, String key, String errorPrefix)
throws IOException {
Object value = map.get(key);
if (value == null) {
throw new IOException(String.format(VALUE_NOT_FOUND_MESSAGE, errorPrefix, key));
}
if (value instanceof BigDecimal) {
BigDecimal bigDecimalValue = (BigDecimal) value;
return bigDecimalValue.intValueExact();
}
if (!(value instanceof Integer)) {
throw new IOException(String.format(VALUE_WRONG_TYPE_MESSAGE, errorPrefix, "integer", key));
}
return (Integer) value;
}

private static String signUsingEsSha256(
PrivateKey privateKey,
JsonFactory jsonFactory,
JsonWebSignature.Header header,
JsonWebToken.Payload payload)
throws GeneralSecurityException, IOException {
String content =
Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(header))

Check warning on line 650 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "Base64"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE-&open=AZy69q6Pgw7BYu5XViE-&pullRequest=1896
+ "."
+ Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(payload));

Check warning on line 652 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "Base64"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViE_&open=AZy69q6Pgw7BYu5XViE_&pullRequest=1896
byte[] contentBytes = StringUtils.getBytesUtf8(content);
byte[] signature =
SecurityUtils.sign(SecurityUtils.getEs256SignatureAlgorithm(), privateKey, contentBytes);

// The JCA returns a DER-encoded signature, but JWS needs the concatenated R|S
// format.
// We need to transcode it. For ES256, the output length is 64 bytes.
byte[] jwsSignature = transcodeDerToConcat(signature, 64);
return content + "." + Base64.encodeBase64URLSafeString(jwsSignature);

Check warning on line 661 in oauth2_http/java/com/google/auth/oauth2/GdchCredentials.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "Base64"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q6Pgw7BYu5XViFA&open=AZy69q6Pgw7BYu5XViFA&pullRequest=1896
}

private static byte[] transcodeDerToConcat(byte[] derSignature, int outputLength)
throws IOException {
if (derSignature.length < 8 || derSignature[0] != 0x30) {
throw new IOException("Invalid DER signature format.");
}

int offset = 2;
int seqLength = derSignature[1] & 0xFF;
if (seqLength == 0x81) {
offset = 3;
seqLength = derSignature[2] & 0xFF;
}

if (derSignature.length - offset != seqLength) {
throw new IOException("Invalid DER signature length.");
}

// R
if (derSignature[offset++] != 0x02) {
throw new IOException("Expected INTEGER for R.");
}
int rLength = derSignature[offset++];
if (derSignature[offset] == 0x00 && rLength > 1 && (derSignature[offset + 1] & 0x80) != 0) {
offset++;
rLength--;
}
byte[] r = new byte[rLength];
System.arraycopy(derSignature, offset, r, 0, rLength);
offset += rLength;

// S
if (derSignature[offset++] != 0x02) {
throw new IOException("Expected INTEGER for S.");
}
int sLength = derSignature[offset++];
if (derSignature[offset] == 0x00 && sLength > 1 && (derSignature[offset + 1] & 0x80) != 0) {
offset++;
sLength--;
}
byte[] s = new byte[sLength];
System.arraycopy(derSignature, offset, s, 0, sLength);

int keySizeBytes = outputLength / 2;
if (r.length > keySizeBytes || s.length > keySizeBytes) {
throw new IOException(
String.format(
"Invalid R or S length. R: %d, S: %d, Expected: %d",
r.length, s.length, keySizeBytes));
}

byte[] result = new byte[outputLength];
System.arraycopy(r, 0, result, keySizeBytes - r.length, r.length);
System.arraycopy(s, 0, result, outputLength - s.length, s.length);

return result;
}
}
17 changes: 15 additions & 2 deletions oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.PemReader;
import com.google.api.client.util.PemReader.Section;
import com.google.api.client.util.SecurityUtils;
import com.google.auth.http.AuthHttpConstants;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.base.Strings;
Expand Down Expand Up @@ -267,6 +266,20 @@ static Map<String, Object> validateMap(Map<String, Object> map, String key, Stri
* key creation.
*/
public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8) throws IOException {
return privateKeyFromPkcs8(privateKeyPkcs8, "RSA");
}

/**
* Converts a PKCS#8 string to a private key of the specified algorithm.
*
* @param privateKeyPkcs8 the PKCS#8 string.
* @param algorithm the algorithm of the private key.
* @return the private key.
* @throws IOException if the PKCS#8 data is invalid or if an unexpected exception occurs during
* key creation.
*/
public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8, String algorithm)
throws IOException {
Reader reader = new StringReader(privateKeyPkcs8);
Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY");
if (section == null) {
Expand All @@ -276,7 +289,7 @@ public static PrivateKey privateKeyFromPkcs8(String privateKeyPkcs8) throws IOEx
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
Exception unexpectedException;
try {
KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory();
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException exception) {
unexpectedException = exception;
Expand Down
20 changes: 20 additions & 0 deletions oauth2_http/javatests/com/google/auth/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ public static Map<String, String> parseQuery(String query) throws IOException {
return map;
}

/**
* Parses the request body as either JSON or a query string.
*
* @param content The request body content.
* @return A map of the parsed parameters.
* @throws IOException If the content cannot be parsed.
*/
public static Map<String, String> parseBody(String content) throws IOException {
if (content != null && content.trim().startsWith("{")) {
GenericJson json = JSON_FACTORY.fromString(content, GenericJson.class);
Map<String, String> map = new HashMap<>();
for (Map.Entry<String, Object> entry : json.entrySet()) {
Object value = entry.getValue();
map.put(entry.getKey(), value == null ? null : value.toString());
}
return map;
}
return parseQuery(content);
}

public static String errorJson(String message) throws IOException {
GenericJson errorResponse = new GenericJson();
errorResponse.setFactory(JSON_FACTORY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
private static final String GDCH_SA_CA_CERT_FILE_NAME = "cert.pem";
private static final String GDCH_SA_CA_CERT_PATH =
GdchCredentialsTest.class.getClassLoader().getResource(GDCH_SA_CA_CERT_FILE_NAME).getPath();
private static final URI GDCH_SA_API_AUDIENCE = URI.create("https://gdch-api-audience");
private static final String GDCH_SA_API_AUDIENCE = "https://gdch-api-audience";
private static final Collection<String> SCOPES = Collections.singletonList("dummy.scope");
private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo");
private static final String QUOTA_PROJECT = "sample-quota-project-id";
Expand Down Expand Up @@ -180,7 +180,7 @@
}

@Test
void getDefaultCredentials_noCredentials_linuxNotGce() throws IOException {

Check warning on line 183 in oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q1lgw7BYu5XViEy&open=AZy69q1lgw7BYu5XViEy&pullRequest=1896

Check warning on line 183 in oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace these 3 tests with a single Parameterized one.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q1lgw7BYu5XViEx&open=AZy69q1lgw7BYu5XViEx&pullRequest=1896
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "Linux");
String productFilePath = SMBIOS_PATH_LINUX;
Expand All @@ -191,7 +191,7 @@
}

@Test
void getDefaultCredentials_static_linux() throws IOException {

Check warning on line 194 in oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q1lgw7BYu5XViEz&open=AZy69q1lgw7BYu5XViEz&pullRequest=1896
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "Linux");
String productFilePath = SMBIOS_PATH_LINUX;
Expand All @@ -203,7 +203,7 @@
}

@Test
void getDefaultCredentials_static_windows_configuredAsLinux_notGce() throws IOException {

Check warning on line 206 in oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q1lgw7BYu5XViE0&open=AZy69q1lgw7BYu5XViE0&pullRequest=1896
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "windows");
String productFilePath = SMBIOS_PATH_LINUX;
Expand All @@ -214,7 +214,7 @@
}

@Test
void getDefaultCredentials_static_unsupportedPlatform_notGce() throws IOException {

Check warning on line 217 in oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.io.IOException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-auth-library-java&issues=AZy69q1lgw7BYu5XViE1&open=AZy69q1lgw7BYu5XViE1&pullRequest=1896
TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider();
testProvider.setProperty("os.name", "macos");
String productFilePath = SMBIOS_PATH_LINUX;
Expand Down
Loading
Loading