cerner-oauth1a 2.0.1 → 2.1.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: 0dfb4c9bf4b9fa35f52b7fad741f860bde8fd792412bbd33e0715371019dd130
4
- data.tar.gz: ea584dbe2eb91a9965249c2f1330cdaa315fba73a4d5e6665e9fd27506ff1dd3
3
+ metadata.gz: cd0cf4f52bc3f90e247bc8c507415f540ababac1483da4c626294328857dc270
4
+ data.tar.gz: 2ae54c85c1ab06b6a3d9581cc062fae1a4a3857037131fbcb479812474f72756
5
5
  SHA512:
6
- metadata.gz: 9371797cb286811967f7e3af33428fc525e4b464bebd1ab550e99ed28daba6a1878eee561959a0469af9937bdb405591548aed1aba9b317137080554fad97f54
7
- data.tar.gz: 65398309db80afb19529f0d2d9988d4c1f061aedc6d6406b4f03eed5e9cae55e6da89605780d2f762532cb77a3964f5c1316868df10fa227f4c0e2c13b79cc3a
6
+ metadata.gz: 429eb27f3433283a68643968c83ab8ff0b0322bd751ac8da475bb62978922dd4e78d110e3561128b91010a8998070b06d286ebd2ccaa87f730f72943340d69e6
7
+ data.tar.gz: 3ada2c0200c845f5710a7eacbf77fbea95a3171a9ca56dc1270265a5d2511c7ee7de62fb58371f23f7eb960ce846ba3b762cce0a9d1292938280eae0177be872
@@ -1,3 +1,10 @@
1
+ # v2.1.0
2
+ Added an attribute for the Protection Realm to Cerner::OAuth1a::AccessTokenAgent,
3
+ Cerner::OAuth1a::AccessToken, and Cerner::OAuth1a::OAuthError. This value will be
4
+ parsed as the canonical root URI of the agent's configured access_token_url. When
5
+ this value is available, it will be added to errors and generated authorization
6
+ headers.
7
+
1
8
  # v2.0.1
2
9
  Allow parsing authorization headers that do not include an oauth_version parameter as per
3
10
  the spec:
@@ -1,3 +1,3 @@
1
1
  * Cerner Corporation
2
2
  * Nathan Beyer [@nbeyer](https://github.com/nbeyer)
3
- * John leacox [@johnlcox](https://github.com/johnlcox)
3
+ * John Leacox [@johnlcox](https://github.com/johnlcox)
@@ -48,7 +48,8 @@ module Cerner
48
48
  timestamp: timestamp,
49
49
  token: token,
50
50
  signature_method: signature_method,
51
- signature: signature
51
+ signature: signature,
52
+ realm: params[:realm]
52
53
  )
53
54
  end
54
55
 
@@ -74,6 +75,8 @@ module Cerner
74
75
  # This value is only populated after a successful #authenticate and only if the #token (oauth_token)
75
76
  # contains a 'Consumer.Principal' parameter.
76
77
  attr_reader :consumer_principal
78
+ # Returns a String, but may be nil, with the Protection Realm related to this token.
79
+ attr_reader :realm
77
80
 
78
81
  # Public: Constructs an instance.
79
82
  #
@@ -93,6 +96,8 @@ module Cerner
93
96
  # Defaults to PLAINTEXT.
94
97
  # :signature - The optional String representing the signature.
95
98
  # Defaults to nil.
99
+ # :realm - The optional String representing the protection realm.
100
+ # Defaults to nil.
96
101
  #
97
102
  # Raises ArgumentError if consumer_key, nonce, timestamp, token or signature_method is nil.
98
103
  def initialize(
@@ -104,7 +109,8 @@ module Cerner
104
109
  signature_method: 'PLAINTEXT',
105
110
  timestamp:,
106
111
  token:,
107
- token_secret: nil
112
+ token_secret: nil,
113
+ realm: nil
108
114
  )
109
115
  raise ArgumentError, 'consumer_key is nil' unless consumer_key
110
116
  raise ArgumentError, 'nonce is nil' unless nonce
@@ -122,6 +128,7 @@ module Cerner
122
128
  @timestamp = convert_to_time(timestamp)
123
129
  @token = token
124
130
  @token_secret = token_secret || nil
131
+ @realm = realm || nil
125
132
  end
126
133
 
127
134
  # Public: Generates a value suitable for use as an HTTP Authorization header. If #signature is
@@ -136,7 +143,7 @@ module Cerner
136
143
  return @authorization_header if @authorization_header
137
144
 
138
145
  unless @signature_method == 'PLAINTEXT'
139
- raise OAuthError.new('signature_method must be PLAINTEXT', nil, 'signature_method_rejected')
146
+ raise OAuthError.new('signature_method must be PLAINTEXT', nil, 'signature_method_rejected', nil, @realm)
140
147
  end
141
148
 
142
149
  if @signature
@@ -144,10 +151,11 @@ module Cerner
144
151
  elsif @accessor_secret && @token_secret
145
152
  sig = "#{@accessor_secret}&#{@token_secret}"
146
153
  else
147
- raise OAuthError.new('accessor_secret or token_secret is nil', nil, 'parameter_absent')
154
+ raise OAuthError.new('accessor_secret or token_secret is nil', nil, 'parameter_absent', nil, @realm)
148
155
  end
149
156
 
150
157
  tuples = {
158
+ realm: @realm,
151
159
  oauth_version: '1.0',
152
160
  oauth_signature_method: @signature_method,
153
161
  oauth_signature: sig,
@@ -174,14 +182,21 @@ module Cerner
174
182
  def authenticate(access_token_agent)
175
183
  raise ArgumentError, 'access_token_agent is nil' unless access_token_agent
176
184
 
185
+ if @realm && !@realm.eql?(access_token_agent.realm)
186
+ raise OAuthError.new('realm does not match provider', nil, 'token_rejected', nil, access_token_agent.realm)
187
+ end
188
+
189
+ # Set realm to the provider's realm if it's not already set
190
+ @realm ||= access_token_agent.realm
191
+
177
192
  unless @signature_method == 'PLAINTEXT'
178
- raise OAuthError.new('signature_method must be PLAINTEXT', nil, 'signature_method_rejected')
193
+ raise OAuthError.new('signature_method must be PLAINTEXT', nil, 'signature_method_rejected', nil, @realm)
179
194
  end
180
195
 
181
196
  tuples = Protocol.parse_url_query_string(@token)
182
197
 
183
198
  unless @consumer_key == tuples.delete(:ConsumerKey)
184
- raise OAuthError.new('consumer keys do not match', nil, 'consumer_key_rejected')
199
+ raise OAuthError.new('consumer keys do not match', nil, 'consumer_key_rejected', nil, @realm)
185
200
  end
186
201
 
187
202
  verify_expiration(tuples.delete(:ExpiresOn))
@@ -232,7 +247,8 @@ module Cerner
232
247
  token == other.token &&
233
248
  token_secret == other.token_secret &&
234
249
  signature_method == other.signature_method &&
235
- signature == other.signature
250
+ signature == other.signature &&
251
+ realm == other.realm
236
252
  end
237
253
 
238
254
  # Public: Compare this to other based on the attributes. Equivalent to calling #==.
@@ -258,7 +274,8 @@ module Cerner
258
274
  token_secret: @token_secret,
259
275
  signature_method: @signature_method,
260
276
  signature: @signature,
261
- consumer_principal: @consumer_principal
277
+ consumer_principal: @consumer_principal,
278
+ realm: @realm
262
279
  }
263
280
  end
264
281
 
@@ -290,7 +307,8 @@ module Cerner
290
307
  'token missing ExpiresOn',
291
308
  nil,
292
309
  'oauth_parameters_rejected',
293
- 'oauth_token'
310
+ 'oauth_token',
311
+ @realm
294
312
  )
295
313
  end
296
314
 
@@ -300,7 +318,9 @@ module Cerner
300
318
  raise OAuthError.new(
301
319
  'token has expired',
302
320
  nil,
303
- 'token_expired'
321
+ 'token_expired',
322
+ nil,
323
+ @realm
304
324
  )
305
325
  end
306
326
  end
@@ -311,14 +331,21 @@ module Cerner
311
331
  'token missing KeysVersion',
312
332
  nil,
313
333
  'oauth_parameters_rejected',
314
- 'oauth_token'
334
+ 'oauth_token',
335
+ @realm
315
336
  )
316
337
  end
317
338
 
318
339
  begin
319
340
  access_token_agent.retrieve_keys(keys_version)
320
341
  rescue OAuthError
321
- raise OAuthError.new('token references invalid keys version', nil, 'oauth_parameters_rejected', 'oauth_token')
342
+ raise OAuthError.new(
343
+ 'token references invalid keys version',
344
+ nil,
345
+ 'oauth_parameters_rejected',
346
+ 'oauth_token',
347
+ @realm
348
+ )
322
349
  end
323
350
  end
324
351
 
@@ -329,7 +356,7 @@ module Cerner
329
356
  # Raises OAuthError if the parameter is not authentic
330
357
  def verify_token(keys)
331
358
  unless keys.verify_rsasha1_signature(@token)
332
- raise OAuthError.new('token is not authentic', nil, 'oauth_parameters_rejected', 'oauth_token')
359
+ raise OAuthError.new('token is not authentic', nil, 'oauth_parameters_rejected', 'oauth_token', @realm)
333
360
  end
334
361
  end
335
362
 
@@ -341,8 +368,12 @@ module Cerner
341
368
  # Raises OAuthError if there is no signature, the parameter is invalid or the signature does
342
369
  # not match the secrets
343
370
  def verify_signature(keys, hmac_secrets)
344
- raise OAuthError.new('missing signature', nil, 'oauth_parameters_absent', 'oauth_signature') unless @signature
345
- raise OAuthError.new('missing HMACSecrets', nil, 'oauth_parameters_rejected', 'oauth_token') unless hmac_secrets
371
+ unless @signature
372
+ raise OAuthError.new('missing signature', nil, 'oauth_parameters_absent', 'oauth_signature', @realm)
373
+ end
374
+ unless hmac_secrets
375
+ raise OAuthError.new('missing HMACSecrets', nil, 'oauth_parameters_rejected', 'oauth_token', @realm)
376
+ end
346
377
 
347
378
  begin
348
379
  secrets = keys.decrypt_hmac_secrets(hmac_secrets)
@@ -351,14 +382,17 @@ module Cerner
351
382
  "unable to decrypt HMACSecrets: #{e.message}",
352
383
  nil,
353
384
  'oauth_parameters_rejected',
354
- 'oauth_token'
385
+ 'oauth_token',
386
+ @realm
355
387
  )
356
388
  end
357
389
 
358
390
  secrets_parts = Protocol.parse_url_query_string(secrets)
359
391
  expected_signature = "#{secrets_parts[:ConsumerSecret]}&#{secrets_parts[:TokenSecret]}"
360
392
 
361
- raise OAuthError.new('signature is not valid', nil, 'signature_invalid') unless @signature == expected_signature
393
+ unless @signature == expected_signature
394
+ raise OAuthError.new('signature is not valid', nil, 'signature_invalid', nil, @realm)
395
+ end
362
396
  end
363
397
  end
364
398
  end
@@ -25,6 +25,8 @@ module Cerner
25
25
  attr_reader :consumer_key
26
26
  # Returns the String Consumer Secret.
27
27
  attr_reader :consumer_secret
28
+ # Returns the String Protection Realm. The realm is root of the access_token_url (scheme + hostname).
29
+ attr_reader :realm
28
30
 
29
31
  # Public: Constructs an instance of the agent.
30
32
  #
@@ -72,6 +74,7 @@ module Cerner
72
74
  @consumer_secret = consumer_secret
73
75
 
74
76
  @access_token_url = convert_to_http_uri(access_token_url)
77
+ @realm = canonical_root_url_for(@access_token_url)
75
78
 
76
79
  @open_timeout = (open_timeout ? open_timeout.to_i : 5)
77
80
  @read_timeout = (read_timeout ? read_timeout.to_i : 5)
@@ -215,6 +218,18 @@ module Cerner
215
218
  uri
216
219
  end
217
220
 
221
+ # Internal: Returns a String containing the canonical root url.
222
+ #
223
+ # url - A URL to get the canonical root url String from.
224
+ #
225
+ # raises ArgumentError if url is nil.
226
+ def canonical_root_url_for(url)
227
+ raise ArgumentError, 'url is nil' unless url
228
+
229
+ realm = URI("#{url.scheme}://#{url.host}:#{url.port}")
230
+ realm.to_s
231
+ end
232
+
218
233
  # Internal: Prepare a request for #retrieve
219
234
  def retrieve_prepare_request(timestamp, nonce, accessor_secret, principal)
220
235
  # construct a POST request
@@ -252,14 +267,15 @@ module Cerner
252
267
  nonce: nonce,
253
268
  timestamp: timestamp,
254
269
  token: tuples[:oauth_token],
255
- token_secret: tuples[:oauth_token_secret]
270
+ token_secret: tuples[:oauth_token_secret],
271
+ realm: @realm
256
272
  )
257
273
  access_token
258
274
  else
259
275
  # Extract any OAuth Problems reported in the response
260
276
  oauth_data = Protocol.parse_authorization_header(response['WWW-Authenticate'])
261
277
  # Raise an error for a failure to acquire a token
262
- raise OAuthError.new('unable to acquire token', response.code, oauth_data[:oauth_problem])
278
+ raise OAuthError.new('unable to acquire token', response.code, oauth_data[:oauth_problem], nil, @realm)
263
279
  end
264
280
  end
265
281
 
@@ -278,10 +294,10 @@ module Cerner
278
294
  when Net::HTTPSuccess
279
295
  parsed_response = JSON.parse(response.body)
280
296
  aes_key = parsed_response.dig('aesKey', 'secretKey')
281
- raise OAuthError, 'AES secret key retrieved was invalid' unless aes_key
297
+ raise OAuthError.new('AES secret key retrieved was invalid', nil, nil, nil, @realm) unless aes_key
282
298
 
283
299
  rsa_key = parsed_response.dig('rsaKey', 'publicKey')
284
- raise OAuthError, 'RSA public key retrieved was invalid' unless rsa_key
300
+ raise OAuthError.new('RSA public key retrieved was invalid', nil, nil, nil, @realm) unless rsa_key
285
301
 
286
302
  Keys.new(
287
303
  version: keys_version,
@@ -292,7 +308,7 @@ module Cerner
292
308
  # Extract any OAuth Problems reported in the response
293
309
  oauth_data = Protocol.parse_authorization_header(response['WWW-Authenticate'])
294
310
  # Raise an error for a failure to acquire keys
295
- raise OAuthError.new('unable to acquire keys', response.code, oauth_data[:oauth_problem])
311
+ raise OAuthError.new('unable to acquire keys', response.code, oauth_data[:oauth_problem], nil, @realm)
296
312
  end
297
313
  end
298
314
  end
@@ -18,6 +18,9 @@ module Cerner
18
18
  # May be nil.
19
19
  attr_reader :oauth_parameters
20
20
 
21
+ # Returns a String with the Protection Realm associated with this error. May be nil.
22
+ attr_reader :realm
23
+
21
24
  # Public: Construct an instance with a message, optional HTTP response code
22
25
  # and optional OAuth Problem string.
23
26
  #
@@ -27,30 +30,35 @@ module Cerner
27
30
  # oauth_parameters - A String/Symbol or Array of Strings/Symbols containing the names of parameters that
28
31
  # are absent or rejected. This is should only be used when oauth_problem
29
32
  # is 'parameter_absent' or 'parameter_rejected' Optional.
33
+ # realm - The protection realm associated with the error. Optional.
30
34
  def initialize(
31
35
  message,
32
36
  http_response_code = nil,
33
37
  oauth_problem = nil,
34
- oauth_parameters = nil
38
+ oauth_parameters = nil,
39
+ realm = nil
35
40
  )
36
41
  @http_response_code = http_response_code
37
42
  @oauth_problem = oauth_problem
38
43
  @oauth_parameters = oauth_parameters ? Array(oauth_parameters) : nil
44
+ @realm = realm
39
45
 
40
46
  parts = []
41
47
  parts << message if message
42
48
  parts << "HTTP #{@http_response_code}" if @http_response_code
43
49
  parts << "OAuth Problem #{@oauth_problem}" if @oauth_problem
44
50
  parts << "OAuth Parameters [#{@oauth_parameters.join(', ')}]" if @oauth_parameters
51
+ parts << "OAuth Realm #{@realm}" if @realm
45
52
  super(parts.empty? ? nil : parts.join(' '))
46
53
  end
47
54
 
48
55
  # Public: Generates an HTTP WWW-Authenticate header value based from the
49
56
  # data in this OAuthError.
50
57
  #
51
- # Returns the generated value or nil if there is no #oauth_problem and #oauth_parameters.
58
+ # Returns the generated value or nil if there is no #oauth_problem or #realm.
52
59
  def to_http_www_authenticate_header
53
60
  params = {}
61
+ params[:realm] = @realm if @realm
54
62
  params[:oauth_problem] = @oauth_problem if @oauth_problem
55
63
 
56
64
  if @oauth_problem && @oauth_parameters
@@ -69,19 +69,22 @@ module Cerner
69
69
  #
70
70
  # params = { realm: 'https://test.host', oauth_problem: 'token_expired' }
71
71
  # Cerner::OAuth1a::Protocol.generate_www_authenticate_header(params)
72
- # # => "OAuth realm=\"https%3A%2F%2Ftest.host\",oauth_problem=\"token_expired\""
72
+ # # => "OAuth realm=\"https://test.host\",oauth_problem=\"token_expired\""
73
73
  #
74
74
  # Returns the String containing the generated value or nil if params is nil or empty.
75
75
  def self.generate_authorization_header(params)
76
76
  return nil unless params && !params.empty?
77
77
 
78
+ realm = "realm=\"#{params.delete(:realm)}\"" if params[:realm]
79
+ realm += ', ' if realm && !params.empty?
80
+
78
81
  encoded_params = params.map do |k, v|
79
82
  k = URI.encode_www_form_component(k).gsub('+', '%20')
80
83
  v = URI.encode_www_form_component(v).gsub('+', '%20')
81
84
  "#{k}=\"#{v}\""
82
85
  end
83
86
 
84
- 'OAuth ' + encoded_params.join(',')
87
+ "OAuth #{realm}#{encoded_params.join(',')}"
85
88
  end
86
89
 
87
90
  # Alias the parse and generate methods
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cerner
4
4
  module OAuth1a
5
- VERSION = '2.0.1'
5
+ VERSION = '2.1.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cerner-oauth1a
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Beyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-31 00:00:00.000000000 Z
11
+ date: 2019-02-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  A minimal dependency library for interacting with a Cerner OAuth 1.0a Access