ibm_cloud_sdk_core 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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