Skip to content
183 changes: 137 additions & 46 deletions Authentication.md

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions src/main/java/com/ibm/cloud/sdk/core/http/HttpClientSingleton.java
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,25 @@ private OkHttpClient setProxyAuthenticator(OkHttpClient client, Authenticator pr
* @return the new {@link OkHttpClient} instance with the logging configured
*/
private OkHttpClient setLoggingLevel(OkHttpClient client, LoggingLevel loggingLevel) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
// First check to see if the client already has the http logging interceptor registered.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While testing the VpcInstanceAuthenticator, I noticed that we had a nasty bug here where we would register a new instance of the logging interceptor EACH TIME the setLoggingLevel method is called, even if the client already had the logging interceptor registered. This would result in multiple copies of the debug output for each request/response.

// If not, then we'll register a new instance of one.
HttpLoggingInterceptor loggingInterceptor = null;
for (Interceptor i : client.networkInterceptors()) {
if (i instanceof HttpLoggingInterceptor) {
loggingInterceptor = (HttpLoggingInterceptor) i;
}
}

OkHttpClient updatedClient = client;
if (loggingInterceptor == null) {
loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.redactHeader(HttpHeaders.AUTHORIZATION);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so that we don't end up logging Authorization header values when debug logging is turned on :)

loggingInterceptor.redactHeader(HttpHeaders.WWW_AUTHENTICATE);
loggingInterceptor.redactHeader("Proxy-Authenticate");
loggingInterceptor.redactHeader("Proxy-Authorization");
OkHttpClient.Builder builder = client.newBuilder().addNetworkInterceptor(loggingInterceptor);
updatedClient = builder.build();
}

switch (loggingLevel) {
case BODY:
Expand All @@ -250,9 +268,8 @@ private OkHttpClient setLoggingLevel(OkHttpClient client, LoggingLevel loggingLe
default:
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
}
OkHttpClient.Builder builder = client.newBuilder().addNetworkInterceptor(loggingInterceptor);

return builder.build();
return updatedClient;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corp. 2015, 2020.
* (C) Copyright IBM Corp. 2015, 2021.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
Expand Down Expand Up @@ -330,7 +330,7 @@ public String toString() {
*
* @return the string
*/
private String toUrl() {
public String toUrl() {
final HttpUrl.Builder builder = httpUrl.newBuilder();
for (final NameValue param : queryParams) {
builder.addQueryParameter(param.getName(), param.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public interface Authenticator {
String AUTHTYPE_CP4D_SERVICE = "cp4dService";
String AUTHTYPE_BEARER_TOKEN = "bearerToken";
String AUTHTYPE_CONTAINER = "container";
String AUTHTYPE_VPC = "vpc";

/**
* Constants which define the names of external config propreties (credential file, environment variable, etc.).
Expand All @@ -50,6 +51,7 @@ public interface Authenticator {
String PROPNAME_EXPIRATION_TIME = "EXPIRATION_TIME";
String PROPNAME_SERVICE_BROKER_SECRET = "SERVICE_BROKER_SECRET";
String PROPNAME_CR_TOKEN_FILENAME = "CR_TOKEN_FILENAME";
String PROPNAME_IAM_PROFILE_CRN = "IAM_PROFILE_CRN";
String PROPNAME_IAM_PROFILE_ID = "IAM_PROFILE_ID";
String PROPNAME_IAM_PROFILE_NAME = "IAM_PROFILE_NAME";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class AuthenticatorBase {
public static final String ERRORMSG_REQ_FAILED = "Error while fetching access token from token service: ";
public static final String ERRORMSG_EXCLUSIVE_PROP_ERROR = "Exactly one of %s or %s must be specified.";
public static final String ERRORMSG_ATLEAST_ONE_PROP_ERROR = "At least one of %s or %s must be specified.";
public static final String ERRORMSG_ATMOST_ONE_PROP_ERROR = "At most one of %s or %s may be specified.";
public static final String ERRORMSG_PROP_INVALID_INTEGER_VALUE =
"The %s property must be a valid integer but was %s.";
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ private static Authenticator createAuthenticator(Map<String, String> props) {
authenticator = IamAuthenticator.fromConfiguration(props);
} else if (authType.equalsIgnoreCase(Authenticator.AUTHTYPE_CONTAINER)) {
authenticator = ContainerAuthenticator.fromConfiguration(props);
} else if (authType.equalsIgnoreCase(Authenticator.AUTHTYPE_VPC)) {
authenticator = VpcInstanceAuthenticator.fromConfiguration(props);
} else if (authType.equalsIgnoreCase(Authenticator.AUTHTYPE_NOAUTH)) {
authenticator = new NoAuthAuthenticator(props);
} else {
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/com/ibm/cloud/sdk/core/security/IamToken.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corp. 2015, 2019.
* (C) Copyright IBM Corp. 2015, 2021.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
Expand All @@ -21,15 +21,23 @@
* Represents response from IAM API.
*/
public class IamToken extends AbstractToken implements ObjectModel, TokenServerResponse {

// These fields are obtained from the IAM token service "getToken" operation response body.
@SerializedName("access_token")
private String accessToken;

@SerializedName("refresh_token")
private String refreshToken;

@SerializedName("token_type")
private String tokenType;

@SerializedName("expires_in")
private Long expiresIn;

private Long expiration;

// The remaining fields are computed rather than obtained from operation response.
private Long refreshTime;

public IamToken() {
Expand All @@ -40,6 +48,16 @@ public IamToken(Throwable t) {
super(t);
}

/**
* Converts a VpcTokenResponse instance to an IamToken instance.
* @param vpcResponse the VpcTokenResponse instance to be converted.
*/
public IamToken(VpcTokenResponse vpcResponse) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this constructor to make it easy to convert a VpcTokenResponse instance (the response received from the VPC Instance Metadata Service) into an IamToken instance (the class we used to cache the iam access token and related info).

accessToken = vpcResponse.getAccessToken();
expiresIn = vpcResponse.getExpiresIn();
expiration = vpcResponse.getExpiresAt().getTime() / 1000;
}

@Override
public String getAccessToken() {
return accessToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.ibm.cloud.sdk.core.http.HttpClientSingleton;
import com.ibm.cloud.sdk.core.http.HttpConfigOptions;
import com.ibm.cloud.sdk.core.http.HttpConfigOptions.LoggingLevel;
import com.ibm.cloud.sdk.core.http.HttpHeaders;
import com.ibm.cloud.sdk.core.http.RequestBuilder;
import com.ibm.cloud.sdk.core.http.ResponseConverter;
Expand All @@ -29,6 +30,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* This class serves as a common base class for Authenticator implementations that interact with a token service
Expand All @@ -45,6 +48,8 @@
public abstract class TokenRequestBasedAuthenticator<T extends AbstractToken, R extends TokenServerResponse>
extends AuthenticatorBase implements Authenticator {

private static final Logger logger = Logger.getLogger(TokenRequestBasedAuthenticator.class.getName());

// Configuration properties that are common to all subclasses.
private boolean disableSSLVerification;
private Map<String, String> headers;
Expand Down Expand Up @@ -252,12 +257,17 @@ protected R invokeRequest(final RequestBuilder requestBuilder, final Class<? ext
final Object[] responseObj = new Object[1];

// Set up the Client we'll use to invoke the request.
final HttpConfigOptions options = new HttpConfigOptions.Builder()
final HttpConfigOptions.Builder clientOptions = new HttpConfigOptions.Builder()
.disableSslVerification(this.disableSSLVerification)
.proxy(this.proxy)
.proxyAuthenticator(this.proxyAuthenticator)
.build();
final OkHttpClient client = HttpClientSingleton.getInstance().configureClient(options);
.proxyAuthenticator(this.proxyAuthenticator);

// Enable request/response logging.
if (logger.isLoggable(Level.FINE)) {
clientOptions.loggingLevel(LoggingLevel.BODY);
}

final OkHttpClient client = HttpClientSingleton.getInstance().configureClient(clientOptions.build());

final Request request = requestBuilder.build();

Expand Down
Loading