Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9b27f08
feat(core): Add initial changes for IAM support
lpatino10 Apr 17, 2018
da47bdc
feat(assistant): Add testing lines for IAM support
lpatino10 Apr 17, 2018
3a841e4
refactor(core): Use proper form to make IAM API call and switch to ha…
lpatino10 Apr 18, 2018
a671643
style(core): Add comments, style changes for clarity
lpatino10 Apr 18, 2018
5f8cf38
refactor(core): Return just the access token string from requestToken…
lpatino10 Apr 18, 2018
8aaae7c
refactor(core): Use constructor instead of builder for IamToken
lpatino10 Apr 18, 2018
d39dbdf
refactor(core): Change options model for IAM and keep user-managed to…
lpatino10 Apr 18, 2018
1bc675e
refactor(core): Don't accept refresh token when instantiating service…
lpatino10 Apr 18, 2018
9b49c96
refactor(core): Replace IAM ServiceCall implementation with a single …
lpatino10 Apr 18, 2018
9b621ad
feat(core): Add method to set user-managed access token outside of co…
lpatino10 Apr 18, 2018
3dffb61
refactor(core): Tweak manner of setting IAM credentials from user POV…
lpatino10 Apr 18, 2018
0c86ade
feat(core): Get IAM API key from VCAP_SERVICES if available
lpatino10 Apr 18, 2018
9e60eeb
test(core): Add test for getting IAM API key from VCAP_SERVICES
lpatino10 Apr 18, 2018
06f61dd
test(core): Add remaining IAM unit tests
lpatino10 Apr 18, 2018
170b94e
refactor(assistant): Remove changes for testing specifically in Assis…
lpatino10 Apr 18, 2018
c0ba4aa
Merge branch 'develop' into iam-support
lpatino10 Apr 19, 2018
722ca5c
feat(service-constructors): Add constructors for IAM authentication
lpatino10 Apr 19, 2018
235f273
refactor(services): Fix merge conflicts
lpatino10 Apr 19, 2018
3341a8d
fix(conversation): Add back method that accidentally got deleted
lpatino10 Apr 19, 2018
34577e1
feat(core): Add check for expired refresh token
lpatino10 Apr 19, 2018
33ab80b
style(nlu): Fix checkstyle complaint
lpatino10 Apr 19, 2018
25c7a7d
fix(core): Fix null check in IAM logic
lpatino10 Apr 19, 2018
f61a9a3
feat(core): Pull IAM URL from VCAP_SERVICES
lpatino10 Apr 19, 2018
772ae6a
style(core): Use string constants for IAM logic
lpatino10 Apr 19, 2018
66773bd
refactor(core): Only expose getToken method in IamTokenManager
lpatino10 Apr 20, 2018
6f80223
docs(main README): Add documentation about using IAM
lpatino10 Apr 20, 2018
31c42de
docs(main README): Address review comments
lpatino10 Apr 20, 2018
f44c5af
refactor(core): Allow tokenManager creation if IAM URL is not in VCAP…
lpatino10 Apr 20, 2018
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
88 changes: 83 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ Java client library to use the [Watson APIs][wdc].
* [Gradle](#gradle)
* [Usage](#usage)
* [Running in IBM Cloud](#running-in-ibm-cloud)
* [Getting the Service Credentials](#getting-the-service-credentials)
* [Authentication](#authentication)
* [Username and Password](#username-and-password)
* [API Key](#api-key)
* [Using IAM](#using-iam)
* IBM Watson Services
* [Assistant](assistant)
* [Discovery](discovery)
Expand Down Expand Up @@ -123,17 +126,92 @@ credentials; the library will get them for you by looking at the [`VCAP_SERVICES
When running in IBM Cloud (or other platforms based on Cloud Foundry), the library will automatically get the credentials from [`VCAP_SERVICES`][vcap_services].
If you have more than one plan, you can use `CredentialUtils` to get the service credentials for an specific plan.

## Getting the Service Credentials
## Authentication

You will need the `username` and `password` (`api_key` for Visual Recognition) credentials, and the API endpoint for each service. Service credentials are different from your IBM Cloud account username and password.
There are three ways to authenticate with IBM Cloud through the SDK: using a `username` and `password`, using an `api_key`, and with IAM.

To get your service credentials, follow these steps:
Getting the credentials necessary for authentication is the same process for all methods. To get them, follow these steps:

1. Log in to [IBM Cloud](https://console.bluemix.net/catalog?category=watson)
1. In the IBM Cloud **Catalog**, select the service you want to use.
1. Click **Create**.
1. On the left side of the page, click **Service Credentials**, and then **View credentials** to view your service credentials.
1. Copy `url`, `username` and `password`(`api_key` for AlchemyAPI or Visual Recognition).
1. Copy the necessary credentials (`url`, `username`, `password`, `api_key`, `apikey`, etc.).

In your code, you can use these values in the service constructor or with a method call after instantiating your service. Here are some examples:

### Username and Password

```java
// in the constructor
Discovery service = new Discovery("2017-11-07", "<username>", "<password>");
```

```java
// after instantiation
Discovery service = new Discovery("2017-11-07");
service.setUsernameAndPassword("<username>", "<password>");
```

### API Key

_Note: This version of instantiation only works with Visual Recognition, as it's the only service that uses an API key rather than a username and password._

```java
// in the constructor
VisualRecognition service = new VisualRecognition("2016-05-20", "<api_key>");
```

```java
// after instantiation
VisualRecognition service = new VisualRecognition("2016-05-20");
service.setApiKey("<api_key>");
```

### Using IAM

When authenticating with IAM, you have the option of passing in:
- the IAM API key and, optionally, the IAM service URL
- an IAM access token

**Be aware that passing in an access token means that you're assuming responsibility for maintaining that token's lifecycle.** If you instead pass in an IAM API key, the SDK will manage it for you.

```java
// in the constructor, letting the SDK manage the IAM token
IamOptions options = new IamOptions.Builder()
.apiKey("<iam_api_key>")
.url("<iam_url>") // optional - the default value is https://iam.ng.bluemix.net/identity/token
.build();
Discovery service = new Discovery("2017-11-07", options);
```

```java
// after instantiation, letting the SDK manage the IAM token
Discovery service = new Discovery("2017-11-07");
IamOptions options = new IamOptions.Builder()
.apiKey("<iam_api_key>")
.build();
service.setIamCredentials(options);
```

```java
// in the constructor, assuming control of managing IAM token
IamOptions options = new IamOptions.Builder()
.accessToken("<access_token>")
.build();
Discovery service = new Discovery("2017-11-07", options);
```

```java
// after instantiation, assuming control of managing IAM token
Discovery service = new Discovery("2017-11-07");
IamOptions options = new IamOptions.Builder()
.accessToken("<access_token>")
.build();
service.setIamCredentials(options);
```

If at any time you would like to let the SDK take over managing your IAM token, simply override your stored IAM credentials with an IAM API key by calling the `setIamCredentials()` method again.
Copy link

Choose a reason for hiding this comment

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

I like that you point this out to the user!


## Android

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import com.ibm.watson.developer_cloud.http.RequestBuilder;
import com.ibm.watson.developer_cloud.http.ServiceCall;
import com.ibm.watson.developer_cloud.service.WatsonService;
import com.ibm.watson.developer_cloud.service.security.IamOptions;
import com.ibm.watson.developer_cloud.util.GsonSingleton;
import com.ibm.watson.developer_cloud.util.ResponseConverterUtils;
import com.ibm.watson.developer_cloud.util.Validator;
Expand Down Expand Up @@ -130,8 +131,20 @@ public Assistant(String versionDate, String username, String password) {
}

/**
* Get response to user input.
* Instantiates a new `Assistant` with IAM. Note that if the access token is specified in the iamOptions,
* you accept responsibility for managing the access token yourself. You must set a new access token before this one
* expires. Failing to do so will result in authentication errors after this token expires.
*
* @param versionDate The version date (yyyy-MM-dd) of the REST API to use. Specifying this value will keep your API
* calls from failing when the service introduces breaking changes.
* @param iamOptions the options for authenticating through IAM
*/
public Assistant(String versionDate, IamOptions iamOptions) {
Copy link
Contributor

Choose a reason for hiding this comment

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

They just need to provide iam_api_key. iam_url (optional) and access_token (optional).

this(versionDate);
setIamCredentials(iamOptions);
}

/**
* Get a response to a user's input. There is no rate limit for this operation.
*
* @param messageOptions the {@link MessageOptions} containing the options for the call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import com.ibm.watson.developer_cloud.http.RequestBuilder;
import com.ibm.watson.developer_cloud.http.ServiceCall;
import com.ibm.watson.developer_cloud.service.WatsonService;
import com.ibm.watson.developer_cloud.service.security.IamOptions;
import com.ibm.watson.developer_cloud.util.GsonSingleton;
import com.ibm.watson.developer_cloud.util.ResponseConverterUtils;
import com.ibm.watson.developer_cloud.util.Validator;
Expand Down Expand Up @@ -130,8 +131,20 @@ public Conversation(String versionDate, String username, String password) {
}

/**
* Get response to user input.
* Instantiates a new `Conversation` with IAM. Note that if the access token is specified in the iamOptions,
* you accept responsibility for managing the access token yourself. You must set a new access token before this one
* expires. Failing to do so will result in authentication errors after this token expires.
*
* @param versionDate The version date (yyyy-MM-dd) of the REST API to use. Specifying this value will keep your API
* calls from failing when the service introduces breaking changes.
* @param iamOptions the options for authenticating through IAM
*/
public Conversation(String versionDate, IamOptions iamOptions) {
this(versionDate);
setIamCredentials(iamOptions);
}

/**
* Get a response to a user's input. There is no rate limit for this operation.
*
* @param messageOptions the {@link MessageOptions} containing the options for the call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import com.ibm.watson.developer_cloud.service.exception.TooManyRequestsException;
import com.ibm.watson.developer_cloud.service.exception.UnauthorizedException;
import com.ibm.watson.developer_cloud.service.exception.UnsupportedException;
import com.ibm.watson.developer_cloud.service.security.IamOptions;
import com.ibm.watson.developer_cloud.service.security.IamTokenManager;
import com.ibm.watson.developer_cloud.util.CredentialUtils;
import com.ibm.watson.developer_cloud.util.RequestUtils;
import com.ibm.watson.developer_cloud.util.ResponseConverterUtils;
Expand Down Expand Up @@ -66,12 +68,14 @@ public abstract class WatsonService {
private static final String MESSAGE_ERROR_3 = "message";
private static final String MESSAGE_ERROR_2 = "error_message";
private static final String BASIC = "Basic ";
private static final String BEARER = "Bearer ";
private static final Logger LOG = Logger.getLogger(WatsonService.class.getName());
private String apiKey;
private String username;
private String password;
private String endPoint;
private final String name;
private IamTokenManager tokenManager;

private OkHttpClient client;

Expand All @@ -97,6 +101,15 @@ public abstract class WatsonService {
*/
public WatsonService(final String name) {
this.name = name;
String iamApiKey = CredentialUtils.getIAMKey(name);
String iamUrl = CredentialUtils.getIAMUrl(name);
if (iamApiKey != null) {
IamOptions iamOptions = new IamOptions.Builder()
.apiKey(iamApiKey)
.url(iamUrl)
.build();
tokenManager = new IamTokenManager(iamOptions);
}
apiKey = CredentialUtils.getAPIKey(name);
String url = CredentialUtils.getAPIUrl(name);
if ((url != null) && !url.isEmpty()) {
Expand Down Expand Up @@ -280,13 +293,17 @@ public void setApiKey(String apiKey) {
* @param builder the new authentication
*/
protected void setAuthentication(final Builder builder) {
if (getApiKey() == null) {
if (tokenManager != null) {
String accessToken = tokenManager.getToken();
builder.addHeader(HttpHeaders.AUTHORIZATION, BEARER + accessToken);
} else if (getApiKey() == null) {
if (skipAuthentication) {
return; // chosen to skip authentication with the service
}
throw new IllegalArgumentException("apiKey or username and password were not specified");
} else {
builder.addHeader(HttpHeaders.AUTHORIZATION, apiKey.startsWith(BASIC) ? apiKey : BASIC + apiKey);
}
builder.addHeader(HttpHeaders.AUTHORIZATION, apiKey.startsWith(BASIC) ? apiKey : BASIC + apiKey);
}

/**
Expand Down Expand Up @@ -325,6 +342,19 @@ public void setDefaultHeaders(final Map<String, String> headers) {
}
}

/**
* Sets IAM information.
*
* Be aware that if you pass in an access token using this method, you accept responsibility for managing the access
* token yourself. You must set a new access token before this one expires. Failing to do so will result in
* authentication errors after this token expires.
*
* @param iamOptions object containing values to be used for authenticating with IAM
*/
public void setIamCredentials(IamOptions iamOptions) {
this.tokenManager = new IamTokenManager(iamOptions);
}

/*
* (non-Javadoc)
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2018 IBM Corp. All Rights Reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.ibm.watson.developer_cloud.service.security;

/**
* Options for authenticating using IAM.
*/
public class IamOptions {
private String apiKey;
private String accessToken;
private String url;

public String getApiKey() {
return apiKey;
}

public String getAccessToken() {
return accessToken;
}

public String getUrl() {
return url;
}

public static class Builder {
private String apiKey;
private String accessToken;
private String url;

public IamOptions build() {
return new IamOptions(this);
}

public Builder apiKey(String apiKey) {
this.apiKey = apiKey;
return this;
}

public Builder accessToken(String accessToken) {
this.accessToken = accessToken;
return this;
}

public Builder url(String url) {
this.url = url;
return this;
}
}

private IamOptions(Builder builder) {
this.apiKey = builder.apiKey;
this.accessToken = builder.accessToken;
this.url = builder.url;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2018 IBM Corp. All Rights Reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.ibm.watson.developer_cloud.service.security;

import com.google.gson.annotations.SerializedName;
import com.ibm.watson.developer_cloud.service.model.ObjectModel;

/**
* Represents response from IAM API.
*/
public class IamToken implements ObjectModel {
@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;

public String getAccessToken() {
return accessToken;
}

public String getRefreshToken() {
return refreshToken;
}

public String getTokenType() {
return tokenType;
}

public Long getExpiresIn() {
return expiresIn;
}

public Long getExpiration() {
return expiration;
}
}
Loading