cerner-oauth1a 2.3.0 → 2.5.3
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 +4 -4
- data/CHANGELOG.md +24 -0
- data/NOTICE +1 -1
- data/README.md +64 -2
- data/lib/cerner/oauth1a.rb +1 -0
- data/lib/cerner/oauth1a/access_token.rb +195 -113
- data/lib/cerner/oauth1a/access_token_agent.rb +66 -63
- data/lib/cerner/oauth1a/cache.rb +13 -5
- data/lib/cerner/oauth1a/cache_rails.rb +3 -9
- data/lib/cerner/oauth1a/internal.rb +95 -0
- data/lib/cerner/oauth1a/protocol.rb +49 -16
- data/lib/cerner/oauth1a/signature.rb +157 -0
- data/lib/cerner/oauth1a/version.rb +1 -1
- metadata +8 -6
@@ -6,6 +6,18 @@ module Cerner
|
|
6
6
|
module OAuth1a
|
7
7
|
# Public: OAuth 1.0a protocol utilities.
|
8
8
|
module Protocol
|
9
|
+
# Public: Encodes the passed text using the percent encoding variant described in the OAuth
|
10
|
+
# 1.0a specification.
|
11
|
+
#
|
12
|
+
# Reference: https://tools.ietf.org/html/rfc5849#section-3.6
|
13
|
+
#
|
14
|
+
# text - A String containing the text to encode.
|
15
|
+
#
|
16
|
+
# Returns a String that has been encoded.
|
17
|
+
def self.percent_encode(text)
|
18
|
+
URI.encode_www_form_component(text).gsub('+', '%20')
|
19
|
+
end
|
20
|
+
|
9
21
|
# Public: Parses a URL-encoded query string into a Hash with symbolized keys.
|
10
22
|
#
|
11
23
|
# query - String containing a URL-encoded query string to parse.
|
@@ -37,6 +49,10 @@ module Cerner
|
|
37
49
|
# Cerner::OAuth1a::Protocol.parse_www_authenticate_header(header)
|
38
50
|
# # => {:realm=>"https://test.host", :oauth_problem=>"token_expired"}
|
39
51
|
#
|
52
|
+
# header = 'OAuth realm="https://test.host", oauth_problem=token_expired'
|
53
|
+
# Cerner::OAuth1a::Protocol.parse_www_authenticate_header(header)
|
54
|
+
# # => {:realm=>"https://test.host", :oauth_problem=>"token_expired"}
|
55
|
+
#
|
40
56
|
# Returns a Hash with symbolized keys of all of the parameters.
|
41
57
|
def self.parse_authorization_header(value)
|
42
58
|
params = {}
|
@@ -45,10 +61,18 @@ module Cerner
|
|
45
61
|
value = value.strip
|
46
62
|
return params unless value.size > 6 && value[0..5].casecmp?('OAuth ')
|
47
63
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
64
|
+
# trim off 'OAuth ' prefix
|
65
|
+
value = value[6..-1]
|
66
|
+
|
67
|
+
# split value on comma separators
|
68
|
+
value.split(/,\s*/).each do |kv_part|
|
69
|
+
# split each part on '=' separator
|
70
|
+
key, value = kv_part.split('=')
|
71
|
+
key = URI.decode_www_form_component(key)
|
72
|
+
# trim off surrounding double quotes, if they exist
|
73
|
+
value = value[1..-2] if value.start_with?('"') && value.end_with?('"')
|
74
|
+
value = URI.decode_www_form_component(value)
|
75
|
+
params[key.to_sym] = value
|
52
76
|
end
|
53
77
|
|
54
78
|
params
|
@@ -73,16 +97,12 @@ module Cerner
|
|
73
97
|
#
|
74
98
|
# Returns the String containing the generated value or nil if params is nil or empty.
|
75
99
|
def self.generate_authorization_header(params)
|
76
|
-
return
|
100
|
+
return unless params && !params.empty?
|
77
101
|
|
78
102
|
realm = "realm=\"#{params.delete(:realm)}\"" if params[:realm]
|
79
|
-
realm += ',
|
103
|
+
realm += ',' if realm && !params.empty?
|
80
104
|
|
81
|
-
encoded_params = params.map
|
82
|
-
k = URI.encode_www_form_component(k).gsub('+', '%20')
|
83
|
-
v = URI.encode_www_form_component(v).gsub('+', '%20')
|
84
|
-
"#{k}=\"#{v}\""
|
85
|
-
end
|
105
|
+
encoded_params = params.map { |k, v| "#{percent_encode(k)}=\"#{percent_encode(v)}\"" }
|
86
106
|
|
87
107
|
"OAuth #{realm}#{encoded_params.join(',')}"
|
88
108
|
end
|
@@ -100,8 +120,12 @@ module Cerner
|
|
100
120
|
# The values come from http://wiki.oauth.net/w/page/12238543/ProblemReporting
|
101
121
|
# and are mapped based on https://oauth.net/core/1.0/#rfc.section.10.
|
102
122
|
BAD_REQUEST_PROBLEMS = %w[
|
103
|
-
additional_authorization_required
|
104
|
-
|
123
|
+
additional_authorization_required
|
124
|
+
parameter_absent
|
125
|
+
parameter_rejected
|
126
|
+
signature_method_rejected
|
127
|
+
timestamp_refused
|
128
|
+
verifier_invalid
|
105
129
|
version_rejected
|
106
130
|
].freeze
|
107
131
|
|
@@ -109,9 +133,18 @@ module Cerner
|
|
109
133
|
# The values come from http://wiki.oauth.net/w/page/12238543/ProblemReporting
|
110
134
|
# and are mapped based on https://oauth.net/core/1.0/#rfc.section.10.
|
111
135
|
UNAUTHORIZED_PROBLEMS = %w[
|
112
|
-
consumer_key_refused
|
113
|
-
|
114
|
-
|
136
|
+
consumer_key_refused
|
137
|
+
consumer_key_rejected
|
138
|
+
consumer_key_unknown
|
139
|
+
nonce_used
|
140
|
+
permission_denied
|
141
|
+
permission_unknown
|
142
|
+
signature_invalid
|
143
|
+
token_expired
|
144
|
+
token_rejected
|
145
|
+
token_revoked
|
146
|
+
token_used
|
147
|
+
user_refused
|
115
148
|
].freeze
|
116
149
|
|
117
150
|
# Public: Converts a oauth_problem value to an HTTP Status using the
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'cerner/oauth1a/protocol'
|
5
|
+
require 'openssl'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module Cerner
|
9
|
+
module OAuth1a
|
10
|
+
# Public: OAuth 1.0a signature utilities.
|
11
|
+
module Signature
|
12
|
+
METHODS = ['PLAINTEXT', 'HMAC-SHA1'].freeze
|
13
|
+
|
14
|
+
# Public: Creates a PLAINTEXT signature.
|
15
|
+
#
|
16
|
+
# Reference: https://tools.ietf.org/html/rfc5849#section-3.4.4
|
17
|
+
#
|
18
|
+
# keywords - The keyword arguments:
|
19
|
+
# :client_shared_secret - Either the Accessor Secret or the Consumer Secret.
|
20
|
+
# :token_shared_secret - The Token Secret.
|
21
|
+
#
|
22
|
+
# Returns a String containing the signature.
|
23
|
+
def self.sign_via_plaintext(client_shared_secret:, token_shared_secret:)
|
24
|
+
client_shared_secret = Protocol.percent_encode(client_shared_secret)
|
25
|
+
token_shared_secret = Protocol.percent_encode(token_shared_secret)
|
26
|
+
"#{client_shared_secret}&#{token_shared_secret}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Creates a HMAC-SHA1 signature.
|
30
|
+
#
|
31
|
+
# Reference: https://tools.ietf.org/html/rfc5849#section-3.4.2
|
32
|
+
#
|
33
|
+
# keywords - The keyword arguments:
|
34
|
+
# :client_shared_secret - Either the Accessor Secret or the Consumer Secret.
|
35
|
+
# :token_shared_secret - The Token Secret.
|
36
|
+
# :signature_base_string - The Signature Base String to sign. See
|
37
|
+
# Signature.build_signature_base_string.
|
38
|
+
#
|
39
|
+
# Returns a String containing the signature.
|
40
|
+
def self.sign_via_hmacsha1(client_shared_secret:, token_shared_secret:, signature_base_string:)
|
41
|
+
client_shared_secret = Protocol.percent_encode(client_shared_secret)
|
42
|
+
token_shared_secret = Protocol.percent_encode(token_shared_secret)
|
43
|
+
signature_key = "#{client_shared_secret}&#{token_shared_secret}"
|
44
|
+
signature = OpenSSL::HMAC.digest('sha1', signature_key, signature_base_string)
|
45
|
+
encoded_signature = Base64.strict_encode64(signature)
|
46
|
+
encoded_signature
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Normalizes a text value as an HTTP method name for use in constructing a Signature
|
50
|
+
# Base String.
|
51
|
+
#
|
52
|
+
# Reference https://tools.ietf.org/html/rfc5849#section-3.4.1.1
|
53
|
+
#
|
54
|
+
# http_method - A String or Symbol containing an HTTP method name.
|
55
|
+
#
|
56
|
+
# Returns the normalized value as a String.
|
57
|
+
#
|
58
|
+
# Raises ArgumentError if http_method is nil.
|
59
|
+
def self.normalize_http_method(http_method)
|
60
|
+
raise ArgumentError, 'http_method is nil' unless http_method
|
61
|
+
|
62
|
+
# accepts Symbol or String
|
63
|
+
Protocol.percent_encode(http_method.to_s.upcase)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Normalizes a fully qualified URL for use as the Base String URI in constructing a
|
67
|
+
# Signature Base String.
|
68
|
+
#
|
69
|
+
# Reference https://tools.ietf.org/html/rfc5849#section-3.4.1.2
|
70
|
+
#
|
71
|
+
# fully_qualified_url - A String or URI that contains the scheme, host, port (optional) and
|
72
|
+
# path of a URL.
|
73
|
+
#
|
74
|
+
# Returns the normalized value as a String.
|
75
|
+
#
|
76
|
+
# Raises ArgumentError if fully_qualified_url is nil.
|
77
|
+
def self.normalize_base_string_uri(fully_qualified_url)
|
78
|
+
raise ArgumentError, 'fully_qualified_url is nil' unless fully_qualified_url
|
79
|
+
|
80
|
+
u = fully_qualified_url.is_a?(URI) ? fully_qualified_url : URI(fully_qualified_url)
|
81
|
+
|
82
|
+
Protocol.percent_encode(URI("#{u.scheme}://#{u.host}:#{u.port}#{u.path}").to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Public: Normalizes the parameters (query string and OAuth parameters) for use as the
|
86
|
+
# request parameters in constructing a Signature Base String.
|
87
|
+
#
|
88
|
+
# Reference: https://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
|
89
|
+
#
|
90
|
+
# params - A Hash of name/value pairs representing the parameters. The keys and values of the
|
91
|
+
# Hash will be assumed to be represented by the value returned from #to_s.
|
92
|
+
#
|
93
|
+
# Returns the normalized value as a String.
|
94
|
+
#
|
95
|
+
# Raises ArgumentError if params is nil.
|
96
|
+
def self.normalize_parameters(params)
|
97
|
+
raise ArgumentError, 'params is nil' unless params
|
98
|
+
|
99
|
+
encoded_params =
|
100
|
+
params.map do |name, value|
|
101
|
+
result = [Protocol.percent_encode(name.to_s), nil]
|
102
|
+
result[1] =
|
103
|
+
if value.is_a?(Array)
|
104
|
+
value = value.map { |e| Protocol.percent_encode(e.to_s) }
|
105
|
+
value.sort
|
106
|
+
else
|
107
|
+
Protocol.percent_encode(value.to_s)
|
108
|
+
end
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
sorted_params = encoded_params.sort_by { |name, _| name }
|
113
|
+
|
114
|
+
exploded_params =
|
115
|
+
sorted_params.map do |pair|
|
116
|
+
name = pair[0]
|
117
|
+
value = pair[1]
|
118
|
+
if value.is_a?(Array)
|
119
|
+
value.map { |e| "#{name}=#{e}" }
|
120
|
+
else
|
121
|
+
"#{name}=#{value}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
exploded_params.flatten!
|
125
|
+
|
126
|
+
joined_params = exploded_params.join('&')
|
127
|
+
Protocol.percent_encode(joined_params)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Public: Builds a Signature Base String.
|
131
|
+
#
|
132
|
+
# keywords - The keyword arguments:
|
133
|
+
# :http_method - A String or Symbol containing an HTTP method name.
|
134
|
+
# :fully_qualified_url - A String or URI that contains the scheme, host, port
|
135
|
+
# (optional) and path of a URL.
|
136
|
+
# :params - A Hash of name/value pairs representing the parameters.
|
137
|
+
# The keys and values of the Hash will be assumed to be
|
138
|
+
# represented by the value returned from #to_s.
|
139
|
+
#
|
140
|
+
# Returns the Signature Base String as a String.
|
141
|
+
#
|
142
|
+
# Raises ArgumentError if http_method, fully_qualified_url or params is nil.
|
143
|
+
def self.build_signature_base_string(http_method:, fully_qualified_url:, params:)
|
144
|
+
raise ArgumentError, 'http_method is nil' unless http_method
|
145
|
+
raise ArgumentError, 'fully_qualified_url is nil' unless fully_qualified_url
|
146
|
+
raise ArgumentError, 'params is nil' unless params
|
147
|
+
|
148
|
+
parts = [
|
149
|
+
normalize_http_method(http_method),
|
150
|
+
normalize_base_string_uri(fully_qualified_url),
|
151
|
+
normalize_parameters(params)
|
152
|
+
]
|
153
|
+
parts.join('&')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
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.3
|
4
|
+
version: 2.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Beyer
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-28 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
|
@@ -30,15 +30,17 @@ files:
|
|
30
30
|
- lib/cerner/oauth1a/access_token_agent.rb
|
31
31
|
- lib/cerner/oauth1a/cache.rb
|
32
32
|
- lib/cerner/oauth1a/cache_rails.rb
|
33
|
+
- lib/cerner/oauth1a/internal.rb
|
33
34
|
- lib/cerner/oauth1a/keys.rb
|
34
35
|
- lib/cerner/oauth1a/oauth_error.rb
|
35
36
|
- lib/cerner/oauth1a/protocol.rb
|
37
|
+
- lib/cerner/oauth1a/signature.rb
|
36
38
|
- lib/cerner/oauth1a/version.rb
|
37
39
|
homepage: http://github.com/cerner/cerner-oauth1a
|
38
40
|
licenses:
|
39
41
|
- Apache-2.0
|
40
42
|
metadata: {}
|
41
|
-
post_install_message:
|
43
|
+
post_install_message:
|
42
44
|
rdoc_options: []
|
43
45
|
require_paths:
|
44
46
|
- lib
|
@@ -53,8 +55,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
55
|
- !ruby/object:Gem::Version
|
54
56
|
version: '0'
|
55
57
|
requirements: []
|
56
|
-
rubygems_version: 3.0.
|
57
|
-
signing_key:
|
58
|
+
rubygems_version: 3.0.8
|
59
|
+
signing_key:
|
58
60
|
specification_version: 4
|
59
61
|
summary: Cerner OAuth 1.0a Consumer and Service Provider Library.
|
60
62
|
test_files: []
|