oauth 0.5.6 → 1.1.5

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +4 -0
  3. data/CHANGELOG.md +848 -0
  4. data/CITATION.cff +20 -0
  5. data/CODE_OF_CONDUCT.md +134 -0
  6. data/CONTRIBUTING.md +218 -0
  7. data/FUNDING.md +77 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +662 -0
  10. data/REEK +2 -0
  11. data/RUBOCOP.md +71 -0
  12. data/SECURITY.md +24 -0
  13. data/lib/oauth/auth_sanitizer.rb +36 -0
  14. data/lib/oauth/client/action_controller_request.rb +33 -22
  15. data/lib/oauth/client/em_http.rb +110 -103
  16. data/lib/oauth/client/helper.rb +87 -82
  17. data/lib/oauth/client/net_http.rb +140 -107
  18. data/lib/oauth/client.rb +2 -0
  19. data/lib/oauth/consumer.rb +222 -141
  20. data/lib/oauth/errors/error.rb +2 -0
  21. data/lib/oauth/errors/problem.rb +4 -1
  22. data/lib/oauth/errors/unauthorized.rb +7 -1
  23. data/lib/oauth/errors.rb +5 -3
  24. data/lib/oauth/helper.rb +48 -18
  25. data/lib/oauth/oauth.rb +31 -7
  26. data/lib/oauth/oauth_test_helper.rb +6 -4
  27. data/lib/oauth/optional.rb +20 -0
  28. data/lib/oauth/request_proxy/action_controller_request.rb +53 -71
  29. data/lib/oauth/request_proxy/action_dispatch_request.rb +42 -4
  30. data/lib/oauth/request_proxy/base.rb +146 -131
  31. data/lib/oauth/request_proxy/curb_request.rb +49 -43
  32. data/lib/oauth/request_proxy/em_http_request.rb +60 -49
  33. data/lib/oauth/request_proxy/jabber_request.rb +19 -9
  34. data/lib/oauth/request_proxy/mock_request.rb +5 -3
  35. data/lib/oauth/request_proxy/net_http.rb +61 -54
  36. data/lib/oauth/request_proxy/rack_request.rb +31 -31
  37. data/lib/oauth/request_proxy/rest_client_request.rb +55 -50
  38. data/lib/oauth/request_proxy/typhoeus_request.rb +51 -45
  39. data/lib/oauth/request_proxy.rb +21 -14
  40. data/lib/oauth/server.rb +18 -12
  41. data/lib/oauth/signature/base.rb +88 -71
  42. data/lib/oauth/signature/hmac/sha1.rb +16 -10
  43. data/lib/oauth/signature/hmac/sha256.rb +16 -10
  44. data/lib/oauth/signature/plaintext.rb +18 -20
  45. data/lib/oauth/signature/rsa/sha1.rb +53 -38
  46. data/lib/oauth/signature.rb +41 -34
  47. data/lib/oauth/token.rb +7 -5
  48. data/lib/oauth/tokens/access_token.rb +6 -4
  49. data/lib/oauth/tokens/consumer_token.rb +11 -7
  50. data/lib/oauth/tokens/request_token.rb +17 -10
  51. data/lib/oauth/tokens/server_token.rb +2 -1
  52. data/lib/oauth/tokens/token.rb +15 -1
  53. data/lib/oauth/version.rb +6 -1
  54. data/lib/oauth.rb +18 -9
  55. data/sig/oauth/consumer.rbs +9 -0
  56. data/sig/oauth/signature/base.rbs +12 -0
  57. data/sig/oauth/tokens/token.rbs +8 -0
  58. data.tar.gz.sig +3 -0
  59. metadata +301 -82
  60. metadata.gz.sig +2 -0
  61. data/LICENSE +0 -20
  62. data/README.rdoc +0 -88
  63. data/TODO +0 -32
  64. data/bin/oauth +0 -11
  65. data/lib/oauth/cli/authorize_command.rb +0 -71
  66. data/lib/oauth/cli/base_command.rb +0 -208
  67. data/lib/oauth/cli/help_command.rb +0 -22
  68. data/lib/oauth/cli/query_command.rb +0 -25
  69. data/lib/oauth/cli/sign_command.rb +0 -81
  70. data/lib/oauth/cli/version_command.rb +0 -7
  71. data/lib/oauth/cli.rb +0 -56
data/lib/oauth/helper.rb CHANGED
@@ -1,9 +1,12 @@
1
- require 'openssl'
2
- require 'base64'
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "openssl"
5
+ require "base64"
3
6
 
4
7
  module OAuth
5
8
  module Helper
6
- extend self
9
+ module_function
7
10
 
8
11
  # Escape +value+ by URL encoding all non-reserved character.
9
12
  #
@@ -15,22 +18,44 @@ module OAuth
15
18
  end
16
19
 
17
20
  def _escape(string)
18
- URI::DEFAULT_PARSER.escape(string, OAuth::RESERVED_CHARACTERS)
21
+ # Percent-encode per RFC 3986 (unreserved: A-Z a-z 0-9 - . _ ~)
22
+ # Encode by byte to ensure stable behavior across Ruby versions and encodings.
23
+ bytes = string.to_s.b.bytes
24
+ bytes.map do |b|
25
+ ch = b.chr
26
+ if ch =~ OAuth::RESERVED_CHARACTERS
27
+ "%%%02X" % b
28
+ else
29
+ ch
30
+ end
31
+ end.join
19
32
  end
20
33
 
21
34
  def unescape(value)
22
- URI::DEFAULT_PARSER.unescape(value.gsub('+', '%2B'))
35
+ # Do NOT treat "+" as space; OAuth treats '+' as a literal plus unless percent-encoded.
36
+ str = value.to_s.gsub("+", "%2B")
37
+ # Decode %HH sequences; leave malformed sequences intact.
38
+ decoded = str.gsub(/%([0-9A-Fa-f]{2})/) { Regexp.last_match(1).to_i(16).chr }
39
+ # Prefer UTF-8 when the decoded bytes form valid UTF-8; otherwise, return as binary.
40
+ begin
41
+ utf8 = decoded.dup
42
+ utf8.force_encoding(Encoding::UTF_8)
43
+ decoded = utf8 if utf8.valid_encoding?
44
+ rescue NameError
45
+ # Older Rubies without Encoding constants: keep original encoding.
46
+ end
47
+ decoded
23
48
  end
24
49
 
25
50
  # Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word
26
51
  # characters removed.
27
- def generate_key(size=32)
28
- Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, '')
52
+ def generate_key(size = 32)
53
+ Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, "")
29
54
  end
30
55
 
31
56
  alias_method :generate_nonce, :generate_key
32
57
 
33
- def generate_timestamp #:nodoc:
58
+ def generate_timestamp # :nodoc:
34
59
  Time.now.to_i.to_s
35
60
  end
36
61
 
@@ -43,22 +68,27 @@ module OAuth
43
68
  # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
44
69
  def normalize(params)
45
70
  params.sort.map do |k, values|
46
- if values.is_a?(Array)
71
+ case values
72
+ when Array
47
73
  # make sure the array has an element so we don't lose the key
48
74
  values << nil if values.empty?
49
75
  # multiple values were provided for a single key
50
- values.sort.collect do |v|
51
- [escape(k),escape(v)] * "="
76
+ if values[0].is_a?(Hash)
77
+ normalize_nested_query(values, k)
78
+ else
79
+ values.sort.collect do |v|
80
+ [escape(k), escape(v)].join("=")
81
+ end
52
82
  end
53
- elsif values.is_a?(Hash)
83
+ when Hash
54
84
  normalize_nested_query(values, k)
55
85
  else
56
- [escape(k),escape(values)] * "="
86
+ [escape(k), escape(values)].join("=")
57
87
  end
58
88
  end * "&"
59
89
  end
60
90
 
61
- #Returns a string representation of the Hash like in URL query string
91
+ # Returns a string representation of the Hash like in URL query string
62
92
  # build_nested_query({:level_1 => {:level_2 => ['value_1','value_2']}}, 'prefix'))
63
93
  # #=> ["prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_1", "prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_2"]
64
94
  def normalize_nested_query(value, prefix = nil)
@@ -72,7 +102,7 @@ module OAuth
72
102
  normalize_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
73
103
  end.flatten.sort
74
104
  else
75
- [escape(prefix), escape(value)] * "="
105
+ [escape(prefix), escape(value)].join("=")
76
106
  end
77
107
  end
78
108
 
@@ -86,16 +116,16 @@ module OAuth
86
116
  #
87
117
  def parse_header(header)
88
118
  # decompose
89
- params = header[6,header.length].split(/[,=&]/)
119
+ params = header[6, header.length].split(/[,=&]/)
90
120
 
91
121
  # odd number of arguments - must be a malformed header.
92
- raise OAuth::Problem.new("Invalid authorization header") if params.size % 2 != 0
122
+ raise OAuth::Problem, "Invalid authorization header" if params.size.odd?
93
123
 
94
124
  params.map! do |v|
95
125
  # strip and unescape
96
126
  val = unescape(v.strip)
97
127
  # strip quotes
98
- val.sub(/^\"(.*)\"$/, '\1')
128
+ val.sub(/^"(.*)"$/, '\1')
99
129
  end
100
130
 
101
131
  # convert into a Hash
data/lib/oauth/oauth.rb CHANGED
@@ -1,13 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth
2
- # request tokens are passed between the consumer and the provider out of
3
- # band (i.e. callbacks cannot be used), per section 6.1.1
4
+ # Out-Of-Band callback token value.
5
+ # OAuth 1.0 and 1.0a both support out-of-band flows, where callbacks cannot be used.
6
+ # See RFC 5849 (OAuth 1.0), Section 6.1.1: Obtaining an Unauthorized Request Token
7
+ # and the 1.0a errata. Providers treating "oob" as the callback URL indicate that
8
+ # the verifier (for 1.0a) will be communicated out of band to the Consumer.
4
9
  OUT_OF_BAND = "oob"
5
10
 
6
- # required parameters, per sections 6.1.1, 6.3.1, and 7
7
- PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token
8
- oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier
9
- oauth_version oauth_signature oauth_body_hash)
11
+ # OAuth parameter keys this library recognizes when normalizing/signing requests.
12
+ # Notes on 1.0 vs 1.0a:
13
+ # - oauth_verifier: Introduced by OAuth 1.0a. Returned to the Consumer after user
14
+ # authorization and required when exchanging a Request Token for an Access Token
15
+ # (Section 6.3.1 in RFC 5849 / 1.0a change).
16
+ # - oauth_callback: Present in 1.0; 1.0a clarified that the Consumer MUST send it when
17
+ # obtaining a Request Token (or use "oob") and that the Service Provider MUST return
18
+ # oauth_callback_confirmed=true with the Request Token response to prevent session
19
+ # fixation attacks. Note that oauth_callback_confirmed is a response parameter, not
20
+ # a request signing parameter, and thus is not listed here.
21
+ # Other keys are common to both 1.0 and 1.0a.
22
+ PARAMETERS = %w[
23
+ oauth_callback
24
+ oauth_consumer_key
25
+ oauth_token
26
+ oauth_signature_method
27
+ oauth_timestamp
28
+ oauth_nonce
29
+ oauth_verifier
30
+ oauth_version
31
+ oauth_signature
32
+ oauth_body_hash
33
+ ].freeze
10
34
 
11
35
  # reserved character regexp, per section 5.1
12
- RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
36
+ RESERVED_CHARACTERS = /[^a-zA-Z0-9\-._~]/.freeze
13
37
  end
@@ -1,5 +1,7 @@
1
- require 'action_controller'
2
- require 'action_controller/test_process'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller"
4
+ require "action_controller/test_process"
3
5
 
4
6
  module OAuth
5
7
  module OAuthTestHelper
@@ -8,7 +10,7 @@ module OAuth
8
10
  incoming.request_uri = request.path
9
11
  incoming.host = request.uri.host
10
12
  incoming.env["SERVER_PORT"] = request.uri.port
11
- incoming.env['REQUEST_METHOD'] = request.http_method
13
+ incoming.env["REQUEST_METHOD"] = request.http_method
12
14
  incoming
13
15
  end
14
16
 
@@ -18,7 +20,7 @@ module OAuth
18
20
  incoming.host = request.uri.host
19
21
  incoming.env["HTTP_AUTHORIZATION"] = request.to_auth_string
20
22
  incoming.env["SERVER_PORT"] = request.uri.port
21
- incoming.env['REQUEST_METHOD'] = request.http_method
23
+ incoming.env["REQUEST_METHOD"] = request.http_method
22
24
  incoming
23
25
  end
24
26
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OAuth
4
+ # Helpers for optional, lazily loaded integrations.
5
+ module Optional
6
+ class << self
7
+ # Try to load EventMachine HTTP client support provided by em-http-request.
8
+ #
9
+ # Returns true if available, false if the dependency is not installed.
10
+ # Never raises LoadError.
11
+ def em_http_available?
12
+ # em-http-request provides "em-http" entrypoint
13
+ require "em-http"
14
+ true
15
+ rescue LoadError
16
+ false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,90 +1,72 @@
1
- require 'active_support'
2
- require "active_support/version"
3
- require 'action_controller'
4
- require 'uri'
1
+ # frozen_string_literal: true
5
2
 
6
- if
7
- Gem::Version.new(ActiveSupport::VERSION::STRING) < Gem::Version.new("3")
8
- then # rails 2.x
9
- require 'action_controller/request'
10
- unless ActionController::Request::HTTP_METHODS.include?("patch")
11
- ActionController::Request::HTTP_METHODS << "patch"
12
- ActionController::Request::HTTP_METHOD_LOOKUP["PATCH"] = :patch
13
- ActionController::Request::HTTP_METHOD_LOOKUP["patch"] = :patch
14
- end
3
+ require "active_support"
4
+ require "action_controller"
5
+ require "uri"
15
6
 
16
- elsif
17
- Gem::Version.new(ActiveSupport::VERSION::STRING) < Gem::Version.new("4")
18
- then # rails 3.x
19
- require 'action_dispatch/http/request'
20
- unless ActionDispatch::Request::HTTP_METHODS.include?("patch")
21
- ActionDispatch::Request::HTTP_METHODS << "patch"
22
- ActionDispatch::Request::HTTP_METHOD_LOOKUP["PATCH"] = :patch
23
- ActionDispatch::Request::HTTP_METHOD_LOOKUP["patch"] = :patch
24
- end
7
+ require "action_dispatch/http/request"
25
8
 
26
- else # rails 4.x and later - already has patch
27
- require 'action_dispatch/http/request'
28
- end
9
+ module OAuth
10
+ module RequestProxy
11
+ class ActionControllerRequest < OAuth::RequestProxy::Base
12
+ proxies(::ActionDispatch::Request)
29
13
 
30
- module OAuth::RequestProxy
31
- class ActionControllerRequest < OAuth::RequestProxy::Base
32
- proxies(defined?(ActionDispatch::AbstractRequest) ? ActionDispatch::AbstractRequest : ActionDispatch::Request)
33
-
34
- def method
35
- request.method.to_s.upcase
36
- end
14
+ def method
15
+ request.method.to_s.upcase
16
+ end
37
17
 
38
- def uri
39
- request.url
40
- end
18
+ def uri
19
+ options[:uri] || request.url
20
+ end
41
21
 
42
- def parameters
43
- if options[:clobber_request]
44
- options[:parameters] || {}
45
- else
46
- params = request_params.merge(query_params).merge(header_params)
47
- params.stringify_keys! if params.respond_to?(:stringify_keys!)
48
- params.merge(options[:parameters] || {})
22
+ def parameters
23
+ if options[:clobber_request]
24
+ options[:parameters] || {}
25
+ else
26
+ # Rails proxies should expose array-style values for params to align with
27
+ # historical oauth gem behavior / specs. Header params remain scalars.
28
+ rq = wrap_values(request_params)
29
+ qq = wrap_values(query_params)
30
+ params = rq.merge(qq).merge(header_params)
31
+ params.stringify_keys! if params.respond_to?(:stringify_keys!)
32
+ params.merge(options[:parameters] || {})
33
+ end
49
34
  end
50
- end
51
35
 
52
- # Override from OAuth::RequestProxy::Base to avoid roundtrip
53
- # conversion to Hash or Array and thus preserve the original
54
- # parameter names
55
- def parameters_for_signature
56
- params = []
57
- params << options[:parameters].to_query if options[:parameters]
36
+ # Override from OAuth::RequestProxy::Base to avoid round-trip
37
+ # conversion to Hash or Array and thus preserve the original
38
+ # parameter names
39
+ def parameters_for_signature
40
+ params = []
41
+ params << options[:parameters].to_query if options[:parameters]
58
42
 
59
- unless options[:clobber_request]
60
- params << header_params.to_query
61
- params << request.query_string unless query_string_blank?
43
+ unless options[:clobber_request]
44
+ params << header_params.to_query
45
+ params << request.query_string unless query_string_blank?
62
46
 
63
- if raw_post_signature?
64
- params << request.raw_post
47
+ params << request.raw_post if raw_post_signature?
65
48
  end
66
- end
67
49
 
68
- params.
69
- join('&').split('&').
70
- reject { |s| s.match(/\A\s*\z/) }.
71
- map { |p| p.split('=').map{|esc| CGI.unescape(esc)} }.
72
- reject { |kv| kv[0] == 'oauth_signature'}
73
- end
50
+ params
51
+ .join("&").split("&")
52
+ .reject { |s| s.match(/\A\s*\z/) }
53
+ .map { |p| p.split("=").map { |esc| CGI.unescape(esc) } }
54
+ .reject { |kv| kv[0] == "oauth_signature" }
55
+ end
74
56
 
75
- def raw_post_signature?
76
- (request.post? || request.put?) && request.content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded")
77
- end
57
+ def raw_post_signature?
58
+ (request.post? || request.put?) && request.content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded")
59
+ end
78
60
 
79
- protected
61
+ protected
80
62
 
81
- def query_params
82
- request.query_parameters
83
- end
63
+ def query_params
64
+ request.query_parameters
65
+ end
84
66
 
85
- def request_params
86
- request.request_parameters
67
+ def request_params
68
+ request.request_parameters
69
+ end
87
70
  end
88
-
89
71
  end
90
72
  end
@@ -1,7 +1,45 @@
1
- require 'oauth/request_proxy/rack_request'
1
+ # frozen_string_literal: true
2
2
 
3
- module OAuth::RequestProxy
4
- class ActionDispatchRequest < OAuth::RequestProxy::RackRequest
5
- proxies ActionDispatch::Request
3
+ require "oauth/request_proxy/rack_request"
4
+
5
+ module OAuth
6
+ module RequestProxy
7
+ class ActionDispatchRequest < OAuth::RequestProxy::RackRequest
8
+ proxies ::ActionDispatch::Request
9
+
10
+ # Prefer the explicitly provided URI, which carries scheme/host info
11
+ # when ActionDispatch env may be minimal in tests.
12
+ def uri
13
+ options[:uri] || super
14
+ end
15
+
16
+ # Rails' ActionDispatch proxy should expose array-style parameters
17
+ # for request/query params to align with legacy oauth gem expectations.
18
+ def parameters
19
+ if options[:clobber_request]
20
+ options[:parameters] || {}
21
+ else
22
+ rq = wrap_values(request_params)
23
+ qq = wrap_values(query_params)
24
+ params = rq.merge(qq).merge(header_params)
25
+ params.merge(options[:parameters] || {})
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def query_params
32
+ # ActionDispatch::Request responds to GET
33
+ request.GET
34
+ end
35
+
36
+ def request_params
37
+ if request.content_type && request.content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded")
38
+ request.POST
39
+ else
40
+ {}
41
+ end
42
+ end
43
+ end
6
44
  end
7
45
  end