cerner-oauth1a 1.0.1 → 2.0.0.rc1

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