cerner-oauth1a 1.0.1 → 2.0.0.rc1

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.
@@ -1,25 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cerner/oauth1a/protocol'
4
+ require 'uri'
5
+
1
6
  module Cerner
2
7
  module OAuth1a
3
8
  # Public: An OAuth-specific error.
4
9
  class OAuthError < StandardError
5
- # Returns the HTTP Response Code, if any, associated with this error.
10
+ # Returns the HTTP Response Code, if any, associated with this error. May be nil.
6
11
  attr_reader :http_response_code
7
- # Returns the OAuth Problem string, if any, associated with this error.
12
+
13
+ # Returns the OAuth Problem string, if any, associated with this error. May be nil.
8
14
  # See http://oauth.pbwiki.com/ProblemReporting for more information.
9
15
  attr_reader :oauth_problem
10
16
 
17
+ # Returns an Array of OAuth parameter names, if any, related to #oauth_problem.
18
+ # May be nil.
19
+ attr_reader :oauth_parameters
20
+
11
21
  # Public: Construct an instance with a message, optional HTTP response code
12
22
  # and optional OAuth Problem string.
13
23
  #
14
24
  # message - A descriptive message, passed to the super class.
15
25
  # http_response_code - The HTTP response code associated with the error. Optional.
16
26
  # oauth_problem - The OAuth Problem string associated with the error. Optional.
17
- def initialize(message, http_response_code=nil, oauth_problem=nil)
27
+ # oauth_parameters - A String/Symbol or Array of Strings/Symbols containing the names of parameters that
28
+ # are absent or rejected. This is should only be used when oauth_problem
29
+ # is 'parameter_absent' or 'parameter_rejected' Optional.
30
+ def initialize(
31
+ message,
32
+ http_response_code = nil,
33
+ oauth_problem = nil,
34
+ oauth_parameters = nil
35
+ )
18
36
  @http_response_code = http_response_code
19
37
  @oauth_problem = oauth_problem
20
- message += " HTTP #{@http_response_code}" if @http_response_code
21
- message += " OAuth Problem #{@oauth_problem}" if @oauth_problem
22
- super message
38
+ @oauth_parameters = oauth_parameters ? Array(oauth_parameters) : nil
39
+
40
+ parts = []
41
+ parts << message if message
42
+ parts << "HTTP #{@http_response_code}" if @http_response_code
43
+ parts << "OAuth Problem #{@oauth_problem}" if @oauth_problem
44
+ parts << "OAuth Parameters [#{@oauth_parameters.join(', ')}]" if @oauth_parameters
45
+ super(parts.empty? ? nil : parts.join(' '))
46
+ end
47
+
48
+ # Public: Generates an HTTP WWW-Authenticate header value based from the
49
+ # data in this OAuthError.
50
+ #
51
+ # Returns the generated value or nil if there is no #oauth_problem and #oauth_parameters.
52
+ def to_http_www_authenticate_header
53
+ params = {}
54
+ params[:oauth_problem] = @oauth_problem if @oauth_problem
55
+
56
+ if @oauth_problem && @oauth_parameters
57
+ case @oauth_problem
58
+ when 'parameter_absent'
59
+ params[:oauth_parameters_absent] = format_parameters(@oauth_parameters)
60
+ when 'parameter_rejected'
61
+ params[:oauth_parameters_rejected] = format_parameters(@oauth_parameters)
62
+ end
63
+ end
64
+
65
+ Protocol.generate_www_authenticate_header(params)
66
+ end
67
+
68
+ # Public: Provides an HTTP Status Symbol based on the #oauth_problem using
69
+ # Protocol.convert_problem_to_http_status.
70
+ #
71
+ # default - The Symbol to return if #oauth_problem contains an unknown value.
72
+ # Defaults to :unauthorized.
73
+ #
74
+ # Returns :unauthorized, :bad_request or the value passed in default parameter.
75
+ def to_http_status(default = :unauthorized)
76
+ Protocol.convert_problem_to_http_status(@oauth_problem, default)
77
+ end
78
+
79
+ private
80
+
81
+ # Internal: Formats a list of parameter names according to the OAuth
82
+ # Problem extension.
83
+ #
84
+ # params - An Array of Strings.
85
+ #
86
+ # Returns a formatted String.
87
+ def format_parameters(params)
88
+ params.map { |p| URI.encode_www_form_component(p).gsub('+', '%20') }.join('&')
23
89
  end
24
90
  end
25
91
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module Cerner
6
+ module OAuth1a
7
+ # Public: OAuth 1.0a protocol utilities.
8
+ module Protocol
9
+ # Public: Parses a URL-encoded query string into a Hash with symbolized keys.
10
+ #
11
+ # query - String containing a URL-encoded query string to parse.
12
+ #
13
+ # Returns a Hash with symbolized keys matching the query parameter names.
14
+ #
15
+ # Raises ArgumentError if query is nil.
16
+ def self.parse_url_query_string(query)
17
+ raise ArgumentError, 'query is nil' unless query
18
+
19
+ Hash[URI.decode_www_form(query).map { |pair| [pair[0].to_sym, pair[1]] }]
20
+ end
21
+
22
+ # Public: Parses an OAuth HTTP Authorization scheme value, which can manifest
23
+ # in either an HTTP Authorization or WWW-Authenticate header.
24
+ #
25
+ # Reference: https://oauth.net/core/1.0a/#auth_header
26
+ #
27
+ # value - String containing the value to parse. If nil or doesn't begin with
28
+ # 'OAuth ', then an empty Hash will be returned.
29
+ #
30
+ # Examples
31
+ #
32
+ # header = 'OAuth oauth_version="1.0", oauth_token="XYZ"'
33
+ # Cerner::OAuth1a::Protocol.parse_authorization_header(header)
34
+ # # => {:oauth_version=>"1.0", :oauth_token=>"XYZ"}
35
+ #
36
+ # header = 'OAuth realm="https://test.host", oauth_problem="token_expired"'
37
+ # Cerner::OAuth1a::Protocol.parse_www_authenticate_header(header)
38
+ # # => {:realm=>"https://test.host", :oauth_problem=>"token_expired"}
39
+ #
40
+ # Returns a Hash with symbolized keys of all of the parameters.
41
+ def self.parse_authorization_header(value)
42
+ params = {}
43
+ return params unless value
44
+
45
+ value = value.strip
46
+ return params unless value.size > 6 && value[0..5].casecmp?('OAuth ')
47
+
48
+ value.scan(/([^,\s=]*)=\"([^\"]*)\"/).each do |pair|
49
+ k = URI.decode_www_form_component(pair[0])
50
+ v = URI.decode_www_form_component(pair[1])
51
+ params[k.to_sym] = v
52
+ end
53
+
54
+ params
55
+ end
56
+
57
+ # Public: Generates an OAuth HTTP Authorization scheme value, which can be
58
+ # in either an HTTP Authorization or WWW-Authenticate header.
59
+ #
60
+ # Reference: https://oauth.net/core/1.0a/#auth_header
61
+ #
62
+ # params - Hash containing the key-value pairs to build the value with.
63
+ #
64
+ # Examples
65
+ #
66
+ # params = { oauth_version: '1.0', oauth_token: 'XYZ' }
67
+ # Cerner::OAuth1a::Protocol.generate_authorization_header(params)
68
+ # # => "OAuth oauth_version=\"1.0\",oauth_token=\"XYZ\""
69
+ #
70
+ # params = { realm: 'https://test.host', oauth_problem: 'token_expired' }
71
+ # Cerner::OAuth1a::Protocol.generate_www_authenticate_header(params)
72
+ # # => "OAuth realm=\"https%3A%2F%2Ftest.host\",oauth_problem=\"token_expired\""
73
+ #
74
+ # Returns the String containing the generated value or nil if params is nil or empty.
75
+ def self.generate_authorization_header(params)
76
+ return nil unless params && !params.empty?
77
+
78
+ encoded_params = params.map do |k, v|
79
+ k = URI.encode_www_form_component(k).gsub('+', '%20')
80
+ v = URI.encode_www_form_component(v).gsub('+', '%20')
81
+ "#{k}=\"#{v}\""
82
+ end
83
+
84
+ 'OAuth ' + encoded_params.join(',')
85
+ end
86
+
87
+ # Alias the parse and generate methods
88
+ class << self
89
+ # Public: Alias for Protocol.parse_authorization_header
90
+ alias parse_www_authenticate_header parse_authorization_header
91
+
92
+ # Public: Alias for Protocol.generate_www_authenticate_header
93
+ alias generate_www_authenticate_header generate_authorization_header
94
+ end
95
+
96
+ # Public: The oauth_problem values that are mapped to HTTP 400 Bad Request.
97
+ # The values come from http://wiki.oauth.net/w/page/12238543/ProblemReporting
98
+ # and are mapped based on https://oauth.net/core/1.0/#rfc.section.10.
99
+ BAD_REQUEST_PROBLEMS = %w[
100
+ additional_authorization_required parameter_absent parameter_rejected
101
+ signature_method_rejected timestamp_refused verifier_invalid
102
+ version_rejected
103
+ ].freeze
104
+
105
+ # Public: The oauth_problem values that are mapped to HTTP 401 Unauthorized.
106
+ # The values come from http://wiki.oauth.net/w/page/12238543/ProblemReporting
107
+ # and are mapped based on https://oauth.net/core/1.0/#rfc.section.10.
108
+ UNAUTHORIZED_PROBLEMS = %w[
109
+ consumer_key_refused consumer_key_rejected consumer_key_unknown
110
+ nonce_used permission_denied permission_unknown signature_invalid
111
+ token_expired token_rejected token_revoked token_used user_refused
112
+ ].freeze
113
+
114
+ # Public: Converts a oauth_problem value to an HTTP Status using the
115
+ # mappings in ::BAD_REQUEST_PROBLEMS and ::UNAUTHORIZED_PROBLEMS.
116
+ #
117
+ # problem - A String containing the oauth_problem value.
118
+ # default - An optional Symbol containing the value to return if an
119
+ # unknown problem value is passed. Defaults to :unauthorized.
120
+ #
121
+ # Returns :unauthorized, :bad_request or the value passed in the default
122
+ # parameter.
123
+ def self.convert_problem_to_http_status(problem, default = :unauthorized)
124
+ return default unless problem
125
+ problem = problem.to_s
126
+ return :unauthorized if UNAUTHORIZED_PROBLEMS.include?(problem)
127
+ return :bad_request if BAD_REQUEST_PROBLEMS.include?(problem)
128
+ default
129
+ end
130
+ end
131
+ end
132
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cerner
2
4
  module OAuth1a
3
- VERSION = '1.0.1'
5
+ VERSION = '2.0.0.rc1'
4
6
  end
5
7
  end
metadata CHANGED
@@ -1,17 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cerner-oauth1a
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Beyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-05 00:00:00.000000000 Z
11
+ date: 2018-04-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A minimal dependency client library for two-legged OAuth 1.0a service
14
- providers, such as Cerner's OAuth 1.0a provider.
13
+ description: |
14
+ A minimal dependency library for interacting with a Cerner OAuth 1.0a Access
15
+ Token Service for invoking Cerner OAuth 1.0a protected services or implementing
16
+ Cerner OAuth 1.0a authentication.
15
17
  email:
16
18
  - nbeyer@gmail.com
17
19
  executables: []
@@ -26,7 +28,10 @@ files:
26
28
  - lib/cerner/oauth1a.rb
27
29
  - lib/cerner/oauth1a/access_token.rb
28
30
  - lib/cerner/oauth1a/access_token_agent.rb
31
+ - lib/cerner/oauth1a/cache.rb
32
+ - lib/cerner/oauth1a/keys.rb
29
33
  - lib/cerner/oauth1a/oauth_error.rb
34
+ - lib/cerner/oauth1a/protocol.rb
30
35
  - lib/cerner/oauth1a/version.rb
31
36
  homepage: http://github.com/cerner/cerner-oauth1a
32
37
  licenses:
@@ -40,16 +45,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
45
  requirements:
41
46
  - - ">="
42
47
  - !ruby/object:Gem::Version
43
- version: '2.2'
48
+ version: '2.4'
44
49
  required_rubygems_version: !ruby/object:Gem::Requirement
45
50
  requirements:
46
- - - ">="
51
+ - - ">"
47
52
  - !ruby/object:Gem::Version
48
- version: '0'
53
+ version: 1.3.1
49
54
  requirements: []
50
55
  rubyforge_project:
51
- rubygems_version: 2.6.8
56
+ rubygems_version: 2.7.6
52
57
  signing_key:
53
58
  specification_version: 4
54
- summary: B2B/two-legged OAuth 1.0a service client.
59
+ summary: Cerner OAuth 1.0a Consumer and Service Provider Library.
55
60
  test_files: []