Skip to content

Conversation

@lpatino10
Copy link
Contributor

@lpatino10 lpatino10 commented Apr 19, 2018

This PR adds in support for IAM in the core of the SDK. With these changes, users have the option of authenticating with Watson services using IAM tokens, which they can manage themselves or let the new IamTokenManager class handle.

Shout out to @dpopp07 for leading the way for this in the Node SDK. Definitely leaned on your changes for my implementation 😉

@codecov-io
Copy link

codecov-io commented Apr 19, 2018

Codecov Report

Merging #929 into develop will increase coverage by 0.01%.
The diff coverage is 39.56%.

Impacted file tree graph

@@              Coverage Diff              @@
##             develop     #929      +/-   ##
=============================================
+ Coverage      38.77%   38.79%   +0.01%     
- Complexity      1421     1438      +17     
=============================================
  Files            559      562       +3     
  Lines          14261    14398     +137     
  Branches         831      842      +11     
=============================================
+ Hits            5530     5586      +56     
- Misses          8415     8487      +72     
- Partials         316      325       +9
Impacted Files Coverage Δ Complexity Δ
...oud/language_translator/v2/LanguageTranslator.java 38.66% <0%> (-1.62%) 8 <0> (ø)
...guage_classifier/v1/NaturalLanguageClassifier.java 58.66% <0%> (-2.45%) 8 <0> (ø)
...eveloper_cloud/speech_to_text/v1/SpeechToText.java 75.43% <0%> (-0.67%) 47 <0> (ø)
...watson/developer_cloud/assistant/v1/Assistant.java 3.41% <0%> (-0.02%) 7 <0> (ø)
...cloud/visual_recognition/v3/VisualRecognition.java 63.23% <0%> (-1.43%) 12 <0> (ø)
...developer_cloud/tone_analyzer/v3/ToneAnalyzer.java 51.21% <0%> (-4.05%) 4 <0> (ø)
...watson/developer_cloud/discovery/v1/Discovery.java 55.63% <0%> (-0.31%) 51 <0> (ø)
...eveloper_cloud/text_to_speech/v1/TextToSpeech.java 73.77% <0%> (-1.86%) 19 <0> (ø)
.../developer_cloud/conversation/v1/Conversation.java 3.41% <0%> (-0.02%) 7 <0> (ø)
...understanding/v1/NaturalLanguageUnderstanding.java 56% <0%> (-3.58%) 8 <0> (ø)
... and 10 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 55f9868...f44c5af. Read the comment docs.

@lpatino10 lpatino10 requested a review from mamoonraja April 19, 2018 14:58
Double fractionOfTimeToLive = 0.8;
Long timeToLive = tokenData.getExpiresIn();
Long expirationTime = tokenData.getExpiration();
Double refreshTime = expirationTime - (timeToLive * (1.0 - fractionOfTimeToLive));
Copy link
Member

Choose a reason for hiding this comment

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

can you review this one more time before merging?

Copy link
Contributor

@germanattanasio germanattanasio left a comment

Choose a reason for hiding this comment

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

We probably want to update the README with examples of how to use IAM.

if (iamApiKey != null) {
tokenManager = new IamTokenManager(new IamOptions.Builder().apiKey(iamApiKey).build());
}
apiKey = CredentialUtils.getAPIKey(name);
Copy link
Contributor

Choose a reason for hiding this comment

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

What about the iam_url? When running on stage1 we need to use a different IAM server

Copy link
Contributor

Choose a reason for hiding this comment

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

We need an CredentialUtils.getIAMUrl(name);

Copy link

Choose a reason for hiding this comment

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

The URL to the IAM endpoint is not present in the VCAP credentials is it? There would be something like "url": "https://gateway-s.watsonplatform.net/assistant/api" but the SDK would have to parse out any info and build the token service URL from there. Is that in the scope of the SDKs? I don't believe I'm doing that in Node.

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to add the URL to the VCAP services.

Copy link

Choose a reason for hiding this comment

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

My apologies for the confusion, I am doing this in Node. Sounds good 👍

if (getApiKey() == null) {
if (tokenManager != null) {
String accessToken = tokenManager.getToken();
builder.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

"Bearer " should be a constant

* 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).


public IamTokenManager(IamOptions options) {
this.apiKey = options.getApiKey();
this.url = (options.getUrl() != null) ? options.getUrl() : "https://iam.ng.bluemix.net/identity/token";
Copy link
Contributor

Choose a reason for hiding this comment

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

use a constant for the URL

builder.header(HttpHeaders.AUTHORIZATION, DEFAULT_AUTHORIZATION);

FormBody formBody = new FormBody.Builder()
.add("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
Copy link
Contributor

Choose a reason for hiding this comment

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

All the strings here should be constants

Copy link
Contributor

@germanattanasio germanattanasio left a comment

Choose a reason for hiding this comment

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

We probably want to update the README with examples of how to use IAM.

Copy link

@dpopp07 dpopp07 left a comment

Choose a reason for hiding this comment

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

@lpatino10 thanks for the shout out 😛

This all looks good to me, as far as the IAM logic goes. Since you're using a builder for the token options, I want to make sure it is clear to the users that they have to manage everything if they use the accessToken() method. It looks like you did a good job of including that in the comments - but I agree with German's suggestion of including examples in the README and maybe reiterating that fact.

"refresh_token": "99999999",
"token_type": "Bearer",
"expires_in": 3600,
"expiration": 999999999999999999
Copy link

Choose a reason for hiding this comment

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

If someone is still maintaining this thousands of years from now, I hope they remember to change this expiration time!

this.name = name;
String iamApiKey = CredentialUtils.getIAMKey(name);
String iamUrl = CredentialUtils.getIAMUrl(name);
if (iamApiKey != null && iamUrl != null) {
Copy link

Choose a reason for hiding this comment

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

Even if the IAM API key is being pulled from VCAP credentials, the IAM URL is still optional, right? I think the tokenManager should still be created if there is an IAM API key but no IAM URL

Copy link
Contributor

Choose a reason for hiding this comment

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

true

Copy link

Choose a reason for hiding this comment

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

@lpatino10 will you address this? As far as I can see, that is all that's left to be done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, I missed this one. Making the change now.

Copy link

@dpopp07 dpopp07 left a comment

Choose a reason for hiding this comment

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

Just had a few suggestions on the documentation

README.md Outdated

### Using IAM

When authenticating with IAM, you have the option of passing in the IAM API key, the IAM URL, and 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.
Copy link

Choose a reason for hiding this comment

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

This is a bit nit-picky, but I would change this to something like "the IAM API key and optionally the IAM service URL, or an IAM access token. The user would not pass in all three, so "and" may be the wrong word to use. Sorry to sound like grammar 👮

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No that's helpful! I was struggling to think of how to convey what was optional or not. I like your idea 👍

README.md Outdated
// in the constructor, letting the SDK manage the IAM token
IamOptions options = new IamOptions.Builder()
.apiKey("<iam_api_key>")
.url("<iam_url>")
Copy link

Choose a reason for hiding this comment

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

Maybe note here that URL is optional and give the default value

README.md Outdated
```

```java
// in the constructor, taking control of managing IAM token
Copy link

Choose a reason for hiding this comment

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

I think "assuming" is more clear here than "taking". Just my opinion

README.md Outdated
// in the constructor, taking control of managing IAM token
IamOptions options = new IamOptions.Builder()
.accessToken("<access_token>")
.url("<iam_url>")
Copy link

Choose a reason for hiding this comment

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

If the user passes in an access token, there is no need to provide the URL. They will be making the service requests, so the URL would never be used.

README.md Outdated
Discovery service = new Discovery("2017-11-07");
IamOptions options = new IamOptions.Builder()
.accessToken("<access_token>")
.url("<iam_url>")
Copy link

Choose a reason for hiding this comment

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

See comments above

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!

Copy link

@dpopp07 dpopp07 left a comment

Choose a reason for hiding this comment

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

Nice! Looks good to me

@lpatino10 lpatino10 merged commit 2a1f307 into develop Apr 20, 2018
@lpatino10 lpatino10 deleted the iam-support branch April 20, 2018 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

6 participants