ibm_cloud_sdk_core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9807ace911b05d47e8d13b72e7623c1ed2a68693cebec40f3d68f2f7d3e437ae
4
- data.tar.gz: db29d041a1ab924b050e357ed6dfc64ed9f806a231fc82d4f48fc8342103adfe
3
+ metadata.gz: b18d557fd57fed55fa59dc6be40502ed3cd5cb5dd38927b1e0086191056412a1
4
+ data.tar.gz: 5d4a983f01f4d9eeaa10dc6970683048ef7b3a0996f9f6de43c1832f04618a4f
5
5
  SHA512:
6
- metadata.gz: 7cd90962533f73d125c1ca06346fe4e49d775c43ef1a0b3cce2a974c389942b793301e98b4be532eaf3e566c2ef7f5737127c865736e322ac2a4f1fa2be92765
7
- data.tar.gz: c271ba5ee551a6121af1f4c4a68b9c2a14ca91f5839cd15ed370b004836b1891fe75a67aed8caa9b4182c5c0bf7ff29024fcd105f6a5f3f688b34e7973dcf9e4
6
+ metadata.gz: 95eb85a507326b86317e5a3209e3086ce1e27f6a849d0d19148b83847da61d71227f12f66268f540b55d0c90c55e052739dc29eb8a86e331a9b783c43062e4ee
7
+ data.tar.gz: ca617c999050ed7e2dca0583e1d62ae018b898ec46feb021875c916433d390a351f1448fbb9d310baa57dc2dfda95ad889c205663ffe4619913a771305d46735
data/README.md CHANGED
@@ -21,6 +21,19 @@ Inside of your Ruby program do:
21
21
  require "ibm_cloud_sdk_core"
22
22
  ```
23
23
 
24
+ ## Authentication Types
25
+ There are several flavors of authentication supported in this package. To specify the intended authentication pattern to use, the user can pass in the parameter `authentication_type`. This parameter is optional, but it may become required in a future major release. The options for this parameter are `basic`, `iam`, and `icp4d`.
26
+
27
+ ### basic
28
+ This indicates Basic Auth is to be used. Users will pass in a `username` and `password` and the SDK will generate a Basic Auth header to send with requests to the service.
29
+
30
+ ### iam
31
+ This indicates that IAM token authentication is to be used. Users can pass in an `iam_apikey` or an `iam_access_token`. If an API key is used, the SDK will manage the token for the user. In either case, the SDK will generate a Bearer Auth header to send with requests to the service.
32
+
33
+ ### icp4d
34
+ This indicates that the service is an instance of ICP4D, which has its own version of token authentication. Users can pass in a `username` and `password`, or an `icp4d_access_token`. If a username and password is given, the SDK will manage the token for the user.
35
+ A `icp4d_url` is **required** for this type. In order to use an SDK-managed token with ICP4D authentication, this option **must** be passed in.
36
+
24
37
  ## Issues
25
38
 
26
39
  If you encounter an issue with this project, you are welcome to submit a [bug report](https://github.com/IBM/ruby-sdk-core/issues).
@@ -7,6 +7,7 @@ require("json")
7
7
  require_relative("./detailed_response.rb")
8
8
  require_relative("./api_exception.rb")
9
9
  require_relative("./iam_token_manager.rb")
10
+ require_relative("./icp4d_token_manager.rb")
10
11
  require_relative("./version.rb")
11
12
 
12
13
  DEFAULT_CREDENTIALS_FILE_NAME = "ibm-credentials.env"
@@ -22,9 +23,12 @@ module IBMCloudSdkCore
22
23
  def initialize(vars)
23
24
  defaults = {
24
25
  vcap_services_name: nil,
26
+ use_vcap_services: true,
27
+ authentication_type: nil,
25
28
  username: nil,
26
29
  password: nil,
27
- use_vcap_services: true,
30
+ icp4d_access_token: nil,
31
+ icp4d_url: nil,
28
32
  iam_apikey: nil,
29
33
  iam_access_token: nil,
30
34
  iam_url: nil,
@@ -34,22 +38,29 @@ module IBMCloudSdkCore
34
38
  }
35
39
  vars = defaults.merge(vars)
36
40
  @url = vars[:url]
37
- @username = nil
38
- @password = nil
39
- @iam_apikey = nil
41
+ @username = vars[:username]
42
+ @password = vars[:password]
43
+ @icp_prefix = vars[:password]&.start_with?("icp-") || vars[:iam_apikey]&.start_with?("icp-") ? true : false
44
+ @icp4d_access_token = vars[:icp4d_access_token]
45
+ @icp4d_url = vars[:icp4d_url]
46
+ @iam_url = vars[:iam_url]
47
+ @iam_apikey = vars[:iam_apikey]
40
48
  @token_manager = nil
49
+ @authentication_type = vars[:authentication_type].downcase unless vars[:authentication_type].nil?
41
50
  @temp_headers = nil
42
- @icp_prefix = vars[:password]&.start_with?("icp-") || vars[:iam_apikey]&.start_with?("icp-") ? true : false
43
51
  @disable_ssl_verification = false
44
52
  @display_name = vars[:display_name]
45
53
 
46
- if (!vars[:iam_access_token].nil? || !vars[:iam_apikey].nil?) && !@icp_prefix
47
- set_token_manager(iam_apikey: vars[:iam_apikey], iam_access_token: vars[:iam_access_token],
54
+ if @authentication_type == "iam" || ((!vars[:iam_access_token].nil? || !vars[:iam_apikey].nil?) && !@icp_prefix)
55
+ iam_token_manager(iam_apikey: vars[:iam_apikey], iam_access_token: vars[:iam_access_token],
48
56
  iam_url: vars[:iam_url], iam_client_id: vars[:iam_client_id],
49
57
  iam_client_secret: vars[:iam_client_secret])
50
58
  elsif !vars[:iam_apikey].nil? && @icp_prefix
51
59
  @username = "apikey"
52
60
  @password = vars[:iam_apikey]
61
+ elsif @authentication_type == "icp4d" || !vars[:icp4d_access_token].nil?
62
+ icp4d_token_manager(icp4d_access_token: vars[:icp4d_access_token], icp4d_url: vars[:icp4d_url],
63
+ username: vars[:username], password: vars[:password])
53
64
  elsif !vars[:username].nil? && !vars[:password].nil?
54
65
  if vars[:username] == "apikey" && !@icp_prefix
55
66
  iam_apikey(iam_apikey: vars[:password])
@@ -73,6 +84,8 @@ module IBMCloudSdkCore
73
84
  @password = @vcap_service_credentials["password"] if @vcap_service_credentials.key?("password")
74
85
  @iam_apikey = @vcap_service_credentials["iam_apikey"] if @vcap_service_credentials.key?("iam_apikey")
75
86
  @iam_access_token = @vcap_service_credentials["iam_access_token"] if @vcap_service_credentials.key?("iam_access_token")
87
+ @icp4d_access_token = @vcap_service_credentials["icp4d_access_token"] if @vcap_service_credentials.key?("icp4d_access_token")
88
+ @icp4d_url = @vcap_service_credentials["icp4d_url"] if @vcap_service_credentials.key?("icp4d_url")
76
89
  @iam_url = @vcap_service_credentials["iam_url"] if @vcap_service_credentials.key?("iam_url")
77
90
  @icp_prefix = @password&.start_with?("icp-") || @iam_apikey&.start_with?("icp-") ? true : false
78
91
  end
@@ -82,6 +95,9 @@ module IBMCloudSdkCore
82
95
  raise ArgumentError.new('The password shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your password') if check_bad_first_or_last_char(@password)
83
96
  raise ArgumentError.new('The url shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your url') if check_bad_first_or_last_char(@url)
84
97
  raise ArgumentError.new('The apikey shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your apikey') if check_bad_first_or_last_char(@iam_apikey)
98
+ raise ArgumentError.new('The iam access token shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your iam access token') if check_bad_first_or_last_char(@iam_access_token)
99
+ raise ArgumentError.new('The icp4d access token shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your icp4d access token') if check_bad_first_or_last_char(@icp4d_access_token)
100
+ raise ArgumentError.new('The icp4d url shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your icp4d url') if check_bad_first_or_last_char(@icp4d_url)
85
101
 
86
102
  @conn = HTTP::Client.new(
87
103
  headers: {}
@@ -248,7 +264,7 @@ module IBMCloudSdkCore
248
264
  return str.start_with?("{", "\"") || str.end_with?("}", "\"") unless str.nil?
249
265
  end
250
266
 
251
- def set_token_manager(iam_apikey: nil, iam_access_token: nil, iam_url: nil,
267
+ def iam_token_manager(iam_apikey: nil, iam_access_token: nil, iam_url: nil,
252
268
  iam_client_id: nil, iam_client_secret: nil)
253
269
  @iam_apikey = iam_apikey
254
270
  @iam_access_token = iam_access_token
@@ -260,6 +276,16 @@ module IBMCloudSdkCore
260
276
  iam_url: iam_url, iam_client_id: iam_client_id, iam_client_secret: iam_client_secret)
261
277
  end
262
278
 
279
+ def icp4d_token_manager(icp4d_access_token: nil, icp4d_url: nil, username: nil, password: nil)
280
+ if !@token_manager.nil?
281
+ @token_manager.access_token(icp4d_access_token)
282
+ else
283
+ raise ArgumentError.new("The icp4d_url is mandatory for ICP4D.") if icp4d_url.nil? && icp4d_access_token.nil?
284
+
285
+ @token_manager = ICP4DTokenManager.new(url: icp4d_url, access_token: icp4d_access_token, username: username, password: password)
286
+ end
287
+ end
288
+
263
289
  def add_timeout(timeout)
264
290
  if timeout.key?(:per_operation)
265
291
  raise TypeError("per_operation in timeout must be a Hash") unless timeout[:per_operation].instance_of?(Hash)
@@ -4,10 +4,11 @@ require("http")
4
4
  require("json")
5
5
  require("rbconfig")
6
6
  require_relative("./version.rb")
7
+ require_relative("./jwt_token_manager")
7
8
 
8
9
  module IBMCloudSdkCore
9
10
  # Class to manage IAM Token Authentication
10
- class IAMTokenManager
11
+ class IAMTokenManager < JWTTokenManager
11
12
  DEFAULT_IAM_URL = "https://iam.cloud.ibm.com/identity/token"
12
13
  CONTENT_TYPE = "application/x-www-form-urlencoded"
13
14
  ACCEPT = "application/json"
@@ -16,7 +17,6 @@ module IBMCloudSdkCore
16
17
  DEFAULT_CLIENT_SECRET = "bx"
17
18
  REQUEST_TOKEN_GRANT_TYPE = "urn:ibm:params:oauth:grant-type:apikey"
18
19
  REQUEST_TOKEN_RESPONSE_TYPE = "cloud_iam"
19
- REFRESH_TOKEN_GRANT_TYPE = "refresh_token"
20
20
 
21
21
  attr_accessor :token_info, :user_access_token
22
22
  def initialize(iam_apikey: nil, iam_access_token: nil, iam_url: nil,
@@ -24,13 +24,8 @@ module IBMCloudSdkCore
24
24
  @iam_apikey = iam_apikey
25
25
  @user_access_token = iam_access_token
26
26
  @iam_url = iam_url.nil? ? DEFAULT_IAM_URL : iam_url
27
- @token_info = {
28
- "access_token" => nil,
29
- "refresh_token" => nil,
30
- "token_type" => nil,
31
- "expires_in" => nil,
32
- "expiration" => nil
33
- }
27
+ super(url: iam_url, access_token: iam_access_token)
28
+
34
29
  # Both the client id and secret should be provided or neither should be provided.
35
30
  if !iam_client_id.nil? && !iam_client_secret.nil?
36
31
  @iam_client_id = iam_client_id
@@ -43,48 +38,6 @@ module IBMCloudSdkCore
43
38
  end
44
39
  end
45
40
 
46
- def request(method:, url:, headers: nil, params: nil, data: nil)
47
- response = nil
48
- if headers.key?("Content-Type") && headers["Content-Type"] == CONTENT_TYPE
49
- response = HTTP.basic_auth(user: @iam_client_id, pass: @iam_client_secret).request(
50
- method,
51
- url,
52
- body: HTTP::URI.form_encode(data),
53
- headers: headers,
54
- params: params
55
- )
56
- end
57
- return JSON.parse(response.body.to_s) if (200..299).cover?(response.code)
58
-
59
- require_relative("./api_exception.rb")
60
- raise ApiException.new(response: response)
61
- end
62
-
63
- # The source of the token is determined by the following logic:
64
- # 1. If user provides their own managed access token, assume it is valid and send it
65
- # 2. If this class is managing tokens and does not yet have one, make a request for one
66
- # 3. If this class is managing tokens and the token has expired refresh it. In case the refresh token is expired, get a new one
67
- # If this class is managing tokens and has a valid token stored, send it
68
- def token
69
- return @user_access_token unless @user_access_token.nil? || (@user_access_token.respond_to?(:empty?) && @user_access_token.empty?)
70
-
71
- if @token_info.all? { |_k, v| v.nil? }
72
- token_info = request_token
73
- save_token_info(
74
- token_info: token_info
75
- )
76
- return @token_info["access_token"]
77
- elsif token_expired?
78
- token_info = refresh_token_expired? ? request_token : refresh_token
79
- save_token_info(
80
- token_info: token_info
81
- )
82
- return @token_info["access_token"]
83
- else
84
- @token_info["access_token"]
85
- end
86
- end
87
-
88
41
  private
89
42
 
90
43
  # Request an IAM token using an API key
@@ -102,60 +55,11 @@ module IBMCloudSdkCore
102
55
  method: "POST",
103
56
  url: @iam_url,
104
57
  headers: headers,
105
- data: data
106
- )
107
- response
108
- end
109
-
110
- # Refresh an IAM token using a refresh token
111
- def refresh_token
112
- headers = {
113
- "Content-Type" => CONTENT_TYPE,
114
- "accept" => ACCEPT
115
- }
116
- data = {
117
- "grant_type" => REFRESH_TOKEN_GRANT_TYPE,
118
- "refresh_token" => @token_info["refresh_token"]
119
- }
120
- response = request(
121
- method: "POST",
122
- url: @iam_url,
123
- headers: headers,
124
- data: data
58
+ data: HTTP::URI.form_encode(data),
59
+ username: @iam_client_id,
60
+ password: @iam_client_secret
125
61
  )
126
62
  response
127
63
  end
128
-
129
- # Check if currently stored token is expired.
130
- # Using a buffer to prevent the edge case of the
131
- # token expiring before the request could be made.
132
- # The buffer will be a fraction of the total TTL. Using 80%.
133
- def token_expired?
134
- return true if @token_info["expiration"].nil? || @token_info["expires_in"].nil?
135
-
136
- fraction_of_ttl = 0.8
137
- time_to_live = @token_info["expires_in"].nil? ? 0 : @token_info["expires_in"]
138
- expire_time = @token_info["expiration"].nil? ? 0 : @token_info["expiration"]
139
- refresh_time = expire_time - (time_to_live * (1.0 - fraction_of_ttl))
140
- current_time = Time.now.to_i
141
- refresh_time < current_time
142
- end
143
-
144
- # Used as a fail-safe to prevent the condition of a refresh token expiring,
145
- # which could happen after around 30 days. This function will return true
146
- # if it has been at least 7 days and 1 hour since the last token was set
147
- def refresh_token_expired?
148
- return true if @token_info["expiration"].nil?
149
-
150
- seven_days = 7 * 24 * 3600
151
- current_time = Time.now.to_i
152
- new_token_time = @token_info["expiration"] + seven_days
153
- new_token_time < current_time
154
- end
155
-
156
- # Save the response from the IAM service request to the object's state
157
- def save_token_info(token_info:)
158
- @token_info = token_info
159
- end
160
64
  end
161
65
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("http")
4
+ require("json")
5
+ require("rbconfig")
6
+ require_relative("./version.rb")
7
+ require_relative("./jwt_token_manager")
8
+
9
+ module IBMCloudSdkCore
10
+ # Class to manage ICP4D Token Authentication
11
+ class ICP4DTokenManager < JWTTokenManager
12
+ def initialize(url: nil, username: nil, password: nil, access_token: nil)
13
+ raise ArgumentError.new("The url is mandatory for ICP4D.") if url.nil? && access_token.nil?
14
+
15
+ url += "/v1/preauth/validateAuth"
16
+ @username = username
17
+ @password = password
18
+ super(url: url, user_access_token: access_token)
19
+ end
20
+
21
+ def request_token
22
+ request(
23
+ method: "GET",
24
+ url: @url,
25
+ username: @username,
26
+ password: @password
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("http")
4
+ require("json")
5
+ require("jwt")
6
+ require("rbconfig")
7
+ require_relative("./version.rb")
8
+
9
+ TOKEN_NAME = "access_token"
10
+
11
+ module IBMCloudSdkCore
12
+ # Class to manage JWT Token Authentication
13
+ class JWTTokenManager
14
+ def initialize(vars)
15
+ defaults = {
16
+ token_info: nil,
17
+ url: nil,
18
+ access_token: nil
19
+ }
20
+ vars = defaults.merge(vars)
21
+
22
+ @url = vars[:url]
23
+ @token_info = vars[:token_info]
24
+ @user_access_token = vars[:access_token]
25
+ @time_to_live = nil
26
+ @expire_time = nil
27
+ @disable_ssl_verification = false
28
+ end
29
+
30
+ def token
31
+ if !@user_access_token.nil?
32
+ @user_access_token
33
+ elsif @token_info.nil? || token_expired?
34
+ token_info = request_token
35
+ save_token_info(token_info: token_info)
36
+ @token_info[TOKEN_NAME]
37
+ elsif !@token_info.nil?
38
+ @token_info[TOKEN_NAME]
39
+ end
40
+ end
41
+
42
+ def access_token(access_token)
43
+ @user_access_token = access_token
44
+ end
45
+
46
+ private
47
+
48
+ # Check if currently stored token is expired.
49
+ # Using a buffer to prevent the edge case of the
50
+ # token expiring before the request could be made.
51
+ # The buffer will be a fraction of the total TTL. Using 80%.
52
+ def token_expired?
53
+ return true if @time_to_live.nil? || @expire_time.nil?
54
+
55
+ fraction_of_ttl = 0.8
56
+ refresh_time = @expire_time - (@time_to_live * (1.0 - fraction_of_ttl))
57
+ current_time = Time.now.to_i
58
+ refresh_time < current_time
59
+ end
60
+
61
+ def save_token_info(token_info: nil)
62
+ access_token = token_info[TOKEN_NAME]
63
+ decoded_response = JWT.decode access_token, nil, false, {}
64
+ exp = decoded_response[0]["exp"]
65
+ iat = decoded_response[0]["iat"]
66
+ @time_to_live = exp - iat
67
+ @expire_time = exp
68
+ @token_info = token_info
69
+ end
70
+
71
+ def request(method:, url:, headers: nil, params: nil, data: nil, username: nil, password: nil)
72
+ response = HTTP.basic_auth(user: username, pass: password).request(
73
+ method,
74
+ url,
75
+ body: data,
76
+ headers: headers,
77
+ params: params
78
+ )
79
+ return JSON.parse(response.body.to_s) if (200..299).cover?(response.code)
80
+
81
+ require_relative("./api_exception.rb")
82
+ raise ApiException.new(response: response)
83
+ end
84
+ end
85
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IBMCloudSdkCore
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require("json")
4
+ require("jwt")
4
5
  require_relative("./../test_helper.rb")
5
6
  require("webmock/minitest")
6
7
 
@@ -209,8 +210,21 @@ class BaseServiceTest < Minitest::Test
209
210
  "created" => "2016-07-11T16:39:01.774Z",
210
211
  "updated" => "2015-12-07T18:53:59.153Z"
211
212
  }
213
+ access_token_layout = {
214
+ "username" => "dummy",
215
+ "role" => "Admin",
216
+ "permissions" => %w[administrator manage_catalog],
217
+ "sub" => "admin",
218
+ "iss" => "sss",
219
+ "aud" => "sss",
220
+ "uid" => "sss",
221
+ "iat" => 3600,
222
+ "exp" => Time.now.to_i
223
+ }
224
+
225
+ access_token = JWT.encode(access_token_layout, "secret", "HS256", "kid": "230498151c214b788dd97f22b85410a5")
212
226
  token_response = {
213
- "access_token" => "oAeisG8yqPY7sFR_x66Z15",
227
+ "access_token" => access_token,
214
228
  "token_type" => "Bearer",
215
229
  "expires_in" => 3600,
216
230
  "expiration" => 1_524_167_011,
@@ -239,7 +253,7 @@ class BaseServiceTest < Minitest::Test
239
253
  stub_request(:get, "https://we.the.best/music")
240
254
  .with(
241
255
  headers: {
242
- "Authorization" => "Bearer oAeisG8yqPY7sFR_x66Z15",
256
+ "Authorization" => "Bearer " + access_token,
243
257
  "Host" => "we.the.best"
244
258
  }
245
259
  ).to_return(status: 200, body: response.to_json, headers: headers)
@@ -252,14 +266,38 @@ class BaseServiceTest < Minitest::Test
252
266
  assert_equal(response, service_response.result)
253
267
  end
254
268
 
269
+ def test_for_icp4d
270
+ service = IBMCloudSdkCore::BaseService.new(
271
+ username: "hello",
272
+ password: "world",
273
+ icp4d_url: "service_url",
274
+ authentication_type: "icp4d"
275
+ )
276
+ refute_nil(service.token_manager)
277
+ end
278
+
255
279
  def test_dummy_request_username_apikey_cred_file
256
280
  response = {
257
281
  "text" => "I want financial advice today.",
258
282
  "created" => "2016-07-11T16:39:01.774Z",
259
283
  "updated" => "2015-12-07T18:53:59.153Z"
260
284
  }
285
+
286
+ access_token_layout = {
287
+ "username" => "dummy",
288
+ "role" => "Admin",
289
+ "permissions" => %w[administrator manage_catalog],
290
+ "sub" => "admin",
291
+ "iss" => "sss",
292
+ "aud" => "sss",
293
+ "uid" => "sss",
294
+ "iat" => 3600,
295
+ "exp" => Time.now.to_i
296
+ }
297
+
298
+ access_token = JWT.encode(access_token_layout, "secret", "HS256", "kid": "230498151c214b788dd97f22b85410a5")
261
299
  token_response = {
262
- "access_token" => "oAeisG8yqPY7sFR_x66Z15",
300
+ "access_token" => access_token,
263
301
  "token_type" => "Bearer",
264
302
  "expires_in" => 3600,
265
303
  "expiration" => 1_524_167_011,
@@ -288,7 +326,7 @@ class BaseServiceTest < Minitest::Test
288
326
  stub_request(:get, "https://we.the.best/music")
289
327
  .with(
290
328
  headers: {
291
- "Authorization" => "Bearer oAeisG8yqPY7sFR_x66Z15",
329
+ "Authorization" => "Bearer " + access_token,
292
330
  "Host" => "we.the.best"
293
331
  }
294
332
  ).to_return(status: 200, body: response.to_json, headers: headers)
@@ -301,4 +339,16 @@ class BaseServiceTest < Minitest::Test
301
339
  service_response = service.request(method: "GET", url: "/music", headers: {})
302
340
  assert_equal(response, service_response.result)
303
341
  end
342
+
343
+ def test_icp4d_access_token
344
+ service = IBMCloudSdkCore::BaseService.new(
345
+ authentication_type: "icp4d",
346
+ icp4d_url: "https://the.sixth.one",
347
+ icp4d_access_token: "token"
348
+ )
349
+ assert_equal(service.instance_variable_get(:@icp4d_access_token), "token")
350
+ service.send :icp4d_token_manager, icp4d_access_token: "new_token", icp4d_url: "the.url"
351
+ token_manager = service.instance_variable_get(:@token_manager)
352
+ assert_equal(token_manager.instance_variable_get(:@user_access_token), "new_token")
353
+ end
304
354
  end
@@ -36,7 +36,7 @@ class IAMTokenManagerTest < Minitest::Test
36
36
  end
37
37
 
38
38
  def test_request_token_fails
39
- iam_url = "https://iam.bluemix.net/identity/token"
39
+ iam_url = "https://iam.cloud.ibm.com/identity/token"
40
40
  token_manager = IBMCloudSdkCore::IAMTokenManager.new(
41
41
  iam_apikey: "iam_apikey",
42
42
  iam_access_token: "iam_access_token",
@@ -46,14 +46,14 @@ class IAMTokenManagerTest < Minitest::Test
46
46
  "code" => "500",
47
47
  "error" => "Oh no"
48
48
  }
49
- stub_request(:post, "https://iam.bluemix.net/identity/token")
49
+ stub_request(:post, "https://iam.cloud.ibm.com/identity/token")
50
50
  .with(
51
51
  body: { "apikey" => "iam_apikey", "grant_type" => "urn:ibm:params:oauth:grant-type:apikey", "response_type" => "cloud_iam" },
52
52
  headers: {
53
53
  "Accept" => "application/json",
54
54
  "Authorization" => "Basic Yng6Yng=",
55
55
  "Content-Type" => "application/x-www-form-urlencoded",
56
- "Host" => "iam.bluemix.net"
56
+ "Host" => "iam.cloud.ibm.com"
57
57
  }
58
58
  ).to_return(status: 500, body: response.to_json, headers: {})
59
59
  assert_raises do
@@ -87,69 +87,19 @@ class IAMTokenManagerTest < Minitest::Test
87
87
  end
88
88
  end
89
89
 
90
- def test_refresh_token
91
- iam_url = "https://iam.cloud.ibm.com/identity/token"
92
- response = {
93
- "access_token" => "oAeisG8yqPY7sFR_x66Z15",
94
- "token_type" => "Bearer",
95
- "expires_in" => 3600,
96
- "expiration" => 1_524_167_011,
97
- "refresh_token" => "jy4gl91BQ"
98
- }
99
- token_manager = IBMCloudSdkCore::IAMTokenManager.new(
100
- iam_apikey: "iam_apikey",
101
- iam_access_token: "iam_access_token",
102
- iam_url: iam_url
103
- )
104
- stub_request(:post, "https://iam.cloud.ibm.com/identity/token")
105
- .with(
106
- body: { "grant_type" => "refresh_token", "refresh_token" => "" },
107
- headers: {
108
- "Accept" => "application/json",
109
- "Authorization" => "Basic Yng6Yng=",
110
- "Content-Type" => "application/x-www-form-urlencoded",
111
- "Host" => "iam.cloud.ibm.com"
112
- }
113
- ).to_return(status: 200, body: response.to_json, headers: {})
114
- token_response = token_manager.send(:refresh_token)
115
- assert_equal(response, token_response)
116
- end
117
-
118
90
  def test_is_token_expired
119
91
  token_manager = IBMCloudSdkCore::IAMTokenManager.new(
120
92
  iam_apikey: "iam_apikey",
121
93
  iam_access_token: "iam_access_token",
122
94
  iam_url: "iam_url"
123
95
  )
124
- token_manager.token_info = {
125
- "access_token" => "oAeisG8yqPY7sFR_x66Z15",
126
- "token_type" => "Bearer",
127
- "expires_in" => 3600,
128
- "expiration" => Time.now.to_i + 6000,
129
- "refresh_token" => "jy4gl91BQ"
130
- }
131
96
 
132
- refute(token_manager.send(:token_expired?))
133
- token_manager.token_info["expiration"] = Time.now.to_i - 3600
134
97
  assert(token_manager.send(:token_expired?))
135
- end
136
-
137
- def test_is_refresh_token_expired
138
- token_manager = IBMCloudSdkCore::IAMTokenManager.new(
139
- iam_apikey: "iam_apikey",
140
- iam_access_token: "iam_access_token",
141
- iam_url: "iam_url"
142
- )
143
- token_manager.token_info = {
144
- "access_token" => "oAeisG8yqPY7sFR_x66Z15",
145
- "token_type" => "Bearer",
146
- "expires_in" => 3600,
147
- "expiration" => Time.now.to_i,
148
- "refresh_token" => "jy4gl91BQ"
149
- }
150
-
151
- refute(token_manager.send(:refresh_token_expired?))
152
- token_manager.token_info["expiration"] = Time.now.to_i - (8 * 24 * 3600)
98
+ token_manager.instance_variable_set(:@time_to_live, 3600)
99
+ token_manager.instance_variable_set(:@expire_time, Time.now.to_i + 6000)
100
+ refute(token_manager.send(:token_expired?))
101
+ token_manager.instance_variable_set(:@time_to_live, 3600)
102
+ token_manager.instance_variable_set(:@expire_time, Time.now.to_i - 3600)
153
103
  assert(token_manager.send(:token_expired?))
154
104
  end
155
105
 
@@ -164,33 +114,31 @@ class IAMTokenManagerTest < Minitest::Test
164
114
  token = token_manager.token
165
115
  assert_equal(token_manager.user_access_token, token)
166
116
 
117
+ access_token_layout = {
118
+ "username" => "dummy",
119
+ "role" => "Admin",
120
+ "permissions" => %w[administrator manage_catalog],
121
+ "sub" => "admin",
122
+ "iss" => "sss",
123
+ "aud" => "sss",
124
+ "uid" => "sss",
125
+ "iat" => 3600,
126
+ "exp" => Time.now.to_i
127
+ }
128
+
129
+ access_token = JWT.encode(access_token_layout, "secret", "HS256", "kid": "230498151c214b788dd97f22b85410a5")
130
+
167
131
  response = {
168
- "access_token" => "hellohello",
132
+ "access_token" => access_token,
169
133
  "token_type" => "Bearer",
170
134
  "expires_in" => 3600,
171
135
  "expiration" => 1_524_167_011,
172
136
  "refresh_token" => "jy4gl91BQ"
173
137
  }
174
- stub_request(:post, "https://iam.cloud.ibm.com/identity/token")
175
- .with(
176
- body: { "apikey" => "iam_apikey", "grant_type" => "urn:ibm:params:oauth:grant-type:apikey", "response_type" => "cloud_iam" },
177
- headers: {
178
- "Accept" => "application/json",
179
- "Authorization" => "Basic Yng6Yng=",
180
- "Content-Type" => "application/x-www-form-urlencoded",
181
- "Host" => "iam.cloud.ibm.com"
182
- }
183
- ).to_return(status: 200, body: response.to_json, headers: {})
184
- token_manager.user_access_token = ""
185
- token = token_manager.token
186
- assert_equal("hellohello", token)
187
-
188
- token_manager.token_info["expiration"] = Time.now.to_i - (20 * 24 * 3600)
189
- token = token_manager.token
190
- assert_equal("hellohello", token)
191
138
 
192
139
  stub_request(:post, "https://iam.cloud.ibm.com/identity/token")
193
140
  .with(
141
+ body: { "apikey" => "iam_apikey", "grant_type" => "urn:ibm:params:oauth:grant-type:apikey", "response_type" => "cloud_iam" },
194
142
  headers: {
195
143
  "Accept" => "application/json",
196
144
  "Authorization" => "Basic Yng6Yng=",
@@ -198,19 +146,8 @@ class IAMTokenManagerTest < Minitest::Test
198
146
  "Host" => "iam.cloud.ibm.com"
199
147
  }
200
148
  ).to_return(status: 200, body: response.to_json, headers: {})
201
- token_manager.token_info["expiration"] = Time.now.to_i - 4000
202
149
  token = token_manager.token
203
- assert_equal("hellohello", token)
204
-
205
- token_manager.token_info = {
206
- "access_token" => "dummy",
207
- "token_type" => "Bearer",
208
- "expires_in" => 3600,
209
- "expiration" => Time.now.to_i + 3600,
210
- "refresh_token" => "jy4gl91BQ"
211
- }
212
- token = token_manager.token
213
- assert_equal("dummy", token)
150
+ assert_equal(token_manager.user_access_token, token)
214
151
  end
215
152
 
216
153
  def test_client_id_only
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("./../test_helper.rb")
4
+ require("webmock/minitest")
5
+
6
+ WebMock.disable_net_connect!(allow_localhost: true)
7
+
8
+ # Unit tests for the ICP4D Token Manager
9
+ class ICP4DTokenManagerTest < Minitest::Test
10
+ def test_request_token
11
+ response = {
12
+ "access_token" => "oAeisG8yqPY7sFR_x66Z15",
13
+ "token_type" => "Bearer",
14
+ "expires_in" => 3600,
15
+ "expiration" => 1_524_167_011,
16
+ "refresh_token" => "jy4gl91BQ"
17
+ }
18
+
19
+ token_manager = IBMCloudSdkCore::ICP4DTokenManager.new(
20
+ url: "https://the.sixth.one",
21
+ username: "you",
22
+ password: "me"
23
+ )
24
+ stub_request(:get, "https://the.sixth.one/v1/preauth/validateAuth")
25
+ .with(
26
+ headers: {
27
+ "Authorization" => "Basic eW91Om1l",
28
+ "Host" => "the.sixth.one"
29
+ }
30
+ ).to_return(status: 200, body: response.to_json, headers: {})
31
+ token_response = token_manager.send(:request_token)
32
+ assert_equal(response, token_response)
33
+ token_manager.access_token("token")
34
+ assert_equal(token_manager.instance_variable_get(:@user_access_token), "token")
35
+ end
36
+
37
+ def test_request_token_fails
38
+ token_manager = IBMCloudSdkCore::ICP4DTokenManager.new(
39
+ url: "https://the.sixth.one",
40
+ username: "you",
41
+ password: "me"
42
+ )
43
+ response = {
44
+ "code" => "500",
45
+ "error" => "Oh no"
46
+ }
47
+ stub_request(:get, "https://the.sixth.one/v1/preauth/validateAuth")
48
+ .with(
49
+ headers: {
50
+ "Authorization" => "Basic eW91Om1l",
51
+ "Host" => "the.sixth.one"
52
+ }
53
+ ).to_return(status: 500, body: response.to_json, headers: {})
54
+ begin
55
+ token_manager.send(:request_token)
56
+ rescue IBMCloudSdkCore::ApiException => e
57
+ assert(e.to_s.instance_of?(String))
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("./../test_helper.rb")
4
+ require("jwt")
5
+ require("webmock/minitest")
6
+
7
+ WebMock.disable_net_connect!(allow_localhost: true)
8
+
9
+ # Unit tests for the JWT Token Manager
10
+ class JWTTokenManagerTest < Minitest::Test
11
+ def test_request_token
12
+ response = {
13
+ "access_token" => "oAeisG8yqPY7sFR_x66Z15",
14
+ "token_type" => "Bearer",
15
+ "expires_in" => 3600,
16
+ "expiration" => 1_524_167_011,
17
+ "refresh_token" => "jy4gl91BQ"
18
+ }
19
+
20
+ token_manager = IBMCloudSdkCore::JWTTokenManager.new(
21
+ icp4d_url: "https://the.sixth.one",
22
+ username: "you",
23
+ password: "me"
24
+ )
25
+ stub_request(:get, "https://the.sixth.one")
26
+ .with(
27
+ headers: {
28
+ "Authorization" => "Basic Og==",
29
+ "Host" => "the.sixth.one"
30
+ }
31
+ ).to_return(status: 200, body: response.to_json, headers: {})
32
+ token_response = token_manager.send(:request, method: "get", url: "https://the.sixth.one")
33
+ assert_equal(response, token_response)
34
+ token_manager.access_token("token")
35
+ assert_equal(token_manager.instance_variable_get(:@user_access_token), "token")
36
+ end
37
+
38
+ def test_request_token_fails
39
+ token_manager = IBMCloudSdkCore::JWTTokenManager.new(
40
+ icp4d_url: "https://the.sixth.one",
41
+ username: "you",
42
+ password: "me"
43
+ )
44
+ response = {
45
+ "code" => "500",
46
+ "error" => "Oh no"
47
+ }
48
+ stub_request(:get, "https://the.sixth.one/")
49
+ .with(
50
+ headers: {
51
+ "Authorization" => "Basic Og==",
52
+ "Host" => "the.sixth.one"
53
+ }
54
+ ).to_return(status: 500, body: response.to_json, headers: {})
55
+ begin
56
+ token_manager.send(:request, method: "get", url: "https://the.sixth.one")
57
+ rescue IBMCloudSdkCore::ApiException => e
58
+ assert(e.to_s.instance_of?(String))
59
+ end
60
+ end
61
+
62
+ def test_request_token_exists
63
+ token_manager = IBMCloudSdkCore::JWTTokenManager.new(
64
+ icp4d_url: "https://the.sixth.one",
65
+ username: "you",
66
+ password: "me",
67
+ access_token: "token"
68
+ )
69
+ token_response = token_manager.send(:token)
70
+ assert_equal("token", token_response)
71
+ end
72
+
73
+ def test_request_token_not_expired
74
+ access_token_layout = {
75
+ "username" => "dummy",
76
+ "role" => "Admin",
77
+ "permissions" => %w[administrator manage_catalog],
78
+ "sub" => "admin",
79
+ "iss" => "sss",
80
+ "aud" => "sss",
81
+ "uid" => "sss",
82
+ "iat" => Time.now.to_i,
83
+ "exp" => Time.now.to_i + (6 * 3600)
84
+ }
85
+ access_token = JWT.encode(access_token_layout, "secret", "HS256", "kid": "230498151c214b788dd97f22b85410a5")
86
+
87
+ token = {
88
+ "access_token" => access_token,
89
+ "token_type" => "Bearer",
90
+ "expires_in" => 3600,
91
+ "expiration" => Time.now.to_i + (6 * 3600),
92
+ "refresh_token" => "jy4gl91BQ"
93
+ }
94
+
95
+ token_manager = IBMCloudSdkCore::JWTTokenManager.new(
96
+ icp4d_url: "https://the.sixth.one",
97
+ username: "you",
98
+ password: "me"
99
+ )
100
+ token_manager.send(:save_token_info, token_info: token)
101
+ token_response = token_manager.send(:token)
102
+ assert_equal(access_token, token_response)
103
+ end
104
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ibm_cloud_sdk_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mamoon Raja
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-07 00:00:00.000000000 Z
11
+ date: 2019-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 4.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: jwt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.2.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.2.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -218,6 +232,8 @@ files:
218
232
  - lib/ibm_cloud_sdk_core/base_service.rb
219
233
  - lib/ibm_cloud_sdk_core/detailed_response.rb
220
234
  - lib/ibm_cloud_sdk_core/iam_token_manager.rb
235
+ - lib/ibm_cloud_sdk_core/icp4d_token_manager.rb
236
+ - lib/ibm_cloud_sdk_core/jwt_token_manager.rb
221
237
  - lib/ibm_cloud_sdk_core/version.rb
222
238
  - rakefile
223
239
  - test/test_helper.rb
@@ -225,6 +241,8 @@ files:
225
241
  - test/unit/test_configure_http_proxy.rb
226
242
  - test/unit/test_detailed_response.rb
227
243
  - test/unit/test_iam_token_manager.rb
244
+ - test/unit/test_icp4d_token_manager.rb
245
+ - test/unit/test_jwt_token_manager.rb
228
246
  homepage: https://www.github.com/IBM
229
247
  licenses:
230
248
  - Apache-2.0