oauth 0.5.14 → 0.6.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 +4 -4
- data/CHANGELOG.md +13 -31
- data/CONTRIBUTING.md +2 -2
- data/README.md +65 -61
- data/SECURITY.md +5 -13
- data/bin/oauth +8 -4
- data/lib/oauth/cli/authorize_command.rb +58 -54
- data/lib/oauth/cli/base_command.rb +163 -159
- data/lib/oauth/cli/help_command.rb +9 -5
- data/lib/oauth/cli/query_command.rb +26 -17
- data/lib/oauth/cli/sign_command.rb +58 -52
- data/lib/oauth/cli/version_command.rb +8 -4
- data/lib/oauth/cli.rb +2 -0
- data/lib/oauth/client/action_controller_request.rb +4 -1
- data/lib/oauth/client/em_http.rb +3 -1
- data/lib/oauth/client/helper.rb +76 -72
- data/lib/oauth/client/net_http.rb +111 -104
- data/lib/oauth/client.rb +2 -0
- data/lib/oauth/consumer.rb +50 -32
- data/lib/oauth/errors/error.rb +2 -0
- data/lib/oauth/errors/problem.rb +3 -0
- data/lib/oauth/errors/unauthorized.rb +4 -0
- data/lib/oauth/errors.rb +2 -0
- data/lib/oauth/helper.rb +9 -5
- data/lib/oauth/oauth.rb +4 -2
- data/lib/oauth/oauth_test_helper.rb +2 -0
- data/lib/oauth/request_proxy/base.rb +4 -4
- data/lib/oauth/request_proxy/mock_request.rb +1 -1
- data/lib/oauth/request_proxy/net_http.rb +8 -8
- data/lib/oauth/request_proxy/rest_client_request.rb +4 -3
- data/lib/oauth/request_proxy.rb +4 -1
- data/lib/oauth/server.rb +8 -4
- data/lib/oauth/signature/base.rb +73 -65
- data/lib/oauth/signature/hmac/sha1.rb +15 -9
- data/lib/oauth/signature/hmac/sha256.rb +15 -9
- data/lib/oauth/signature/plaintext.rb +18 -20
- data/lib/oauth/signature/rsa/sha1.rb +46 -38
- data/lib/oauth/signature.rb +3 -0
- data/lib/oauth/token.rb +2 -0
- data/lib/oauth/tokens/access_token.rb +2 -0
- data/lib/oauth/tokens/consumer_token.rb +2 -0
- data/lib/oauth/tokens/request_token.rb +5 -2
- data/lib/oauth/tokens/server_token.rb +2 -0
- data/lib/oauth/tokens/token.rb +2 -0
- data/lib/oauth/version.rb +5 -1
- data/lib/oauth.rb +8 -2
- metadata +28 -56
data/lib/oauth/errors.rb
CHANGED
data/lib/oauth/helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
1
4
|
require "openssl"
|
2
5
|
require "base64"
|
3
6
|
|
4
7
|
module OAuth
|
5
8
|
module Helper
|
6
|
-
|
9
|
+
module_function
|
7
10
|
|
8
11
|
# Escape +value+ by URL encoding all non-reserved character.
|
9
12
|
#
|
@@ -30,7 +33,7 @@ module OAuth
|
|
30
33
|
|
31
34
|
alias generate_nonce generate_key
|
32
35
|
|
33
|
-
def generate_timestamp
|
36
|
+
def generate_timestamp # :nodoc:
|
34
37
|
Time.now.to_i.to_s
|
35
38
|
end
|
36
39
|
|
@@ -43,7 +46,8 @@ module OAuth
|
|
43
46
|
# See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
|
44
47
|
def normalize(params)
|
45
48
|
params.sort.map do |k, values|
|
46
|
-
|
49
|
+
case values
|
50
|
+
when Array
|
47
51
|
# make sure the array has an element so we don't lose the key
|
48
52
|
values << nil if values.empty?
|
49
53
|
# multiple values were provided for a single key
|
@@ -54,7 +58,7 @@ module OAuth
|
|
54
58
|
[escape(k), escape(v)].join("=")
|
55
59
|
end
|
56
60
|
end
|
57
|
-
|
61
|
+
when Hash
|
58
62
|
normalize_nested_query(values, k)
|
59
63
|
else
|
60
64
|
[escape(k), escape(values)].join("=")
|
@@ -99,7 +103,7 @@ module OAuth
|
|
99
103
|
# strip and unescape
|
100
104
|
val = unescape(v.strip)
|
101
105
|
# strip quotes
|
102
|
-
val.sub(
|
106
|
+
val.sub(/^"(.*)"$/, '\1')
|
103
107
|
end
|
104
108
|
|
105
109
|
# convert into a Hash
|
data/lib/oauth/oauth.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth
|
2
4
|
# request tokens are passed between the consumer and the provider out of
|
3
5
|
# band (i.e. callbacks cannot be used), per section 6.1.1
|
4
|
-
OUT_OF_BAND = "oob"
|
6
|
+
OUT_OF_BAND = "oob"
|
5
7
|
|
6
8
|
# required parameters, per sections 6.1.1, 6.3.1, and 7
|
7
9
|
PARAMETERS = %w[oauth_callback oauth_consumer_key oauth_token
|
@@ -9,5 +11,5 @@ module OAuth
|
|
9
11
|
oauth_version oauth_signature oauth_body_hash].freeze
|
10
12
|
|
11
13
|
# reserved character regexp, per section 5.1
|
12
|
-
RESERVED_CHARACTERS = /[^a-zA-Z0-9
|
14
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-._~]/.freeze
|
13
15
|
end
|
@@ -79,15 +79,15 @@ module OAuth
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def parameters_for_signature
|
82
|
-
parameters.
|
82
|
+
parameters.select { |k, _v| !signature_and_unsigned_parameters.include?(k) }
|
83
83
|
end
|
84
84
|
|
85
85
|
def oauth_parameters
|
86
|
-
parameters.select { |k,
|
86
|
+
parameters.select { |k, v| OAuth::PARAMETERS.include?(k) && !v.nil? && v != "" }
|
87
87
|
end
|
88
88
|
|
89
89
|
def non_oauth_parameters
|
90
|
-
parameters.
|
90
|
+
parameters.select { |k, _v| !OAuth::PARAMETERS.include?(k) }
|
91
91
|
end
|
92
92
|
|
93
93
|
def signature_and_unsigned_parameters
|
@@ -127,7 +127,7 @@ module OAuth
|
|
127
127
|
end
|
128
128
|
|
129
129
|
# URI, including OAuth parameters
|
130
|
-
def signed_uri(with_oauth
|
130
|
+
def signed_uri(with_oauth: true)
|
131
131
|
if signed?
|
132
132
|
params = if with_oauth
|
133
133
|
parameters
|
@@ -38,13 +38,11 @@ module OAuth
|
|
38
38
|
request_params = CGI.parse(query_string)
|
39
39
|
# request_params.each{|k,v| request_params[k] = [nil] if v == []}
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
request_params[k] = [v]
|
47
|
-
end
|
41
|
+
options[:parameters]&.each do |k, v|
|
42
|
+
if request_params.key?(k) && v
|
43
|
+
request_params[k] << v
|
44
|
+
else
|
45
|
+
request_params[k] = [v]
|
48
46
|
end
|
49
47
|
end
|
50
48
|
request_params
|
@@ -71,7 +69,9 @@ module OAuth
|
|
71
69
|
end
|
72
70
|
|
73
71
|
def auth_header_params
|
74
|
-
|
72
|
+
unless request["Authorization"] && request["Authorization"][0, 5] == "OAuth"
|
73
|
+
return nil
|
74
|
+
end
|
75
75
|
|
76
76
|
request["Authorization"]
|
77
77
|
end
|
@@ -38,7 +38,8 @@ module OAuth
|
|
38
38
|
|
39
39
|
def post_parameters
|
40
40
|
# Post params are only used if posting form data
|
41
|
-
|
41
|
+
is_form_data = request.payload && request.payload.headers["Content-Type"] == "application/x-www-form-urlencoded"
|
42
|
+
if is_form_data && (method == "POST" || method == "PUT")
|
42
43
|
OAuth::Helper.stringify_keys(query_string_to_hash(request.payload.to_s) || {})
|
43
44
|
else
|
44
45
|
{}
|
@@ -51,9 +52,9 @@ module OAuth
|
|
51
52
|
query.split("&").inject({}) do |result, q|
|
52
53
|
k, v = q.split("=")
|
53
54
|
if !v.nil?
|
54
|
-
result.merge(k => v)
|
55
|
+
result.merge({ k => v })
|
55
56
|
elsif !result.key?(k)
|
56
|
-
result.merge(k => true)
|
57
|
+
result.merge({ k => true })
|
57
58
|
else
|
58
59
|
result
|
59
60
|
end
|
data/lib/oauth/request_proxy.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth
|
2
4
|
module RequestProxy
|
3
|
-
def self.available_proxies
|
5
|
+
def self.available_proxies # :nodoc:
|
4
6
|
@available_proxies ||= {}
|
5
7
|
end
|
6
8
|
|
@@ -16,6 +18,7 @@ module OAuth
|
|
16
18
|
end
|
17
19
|
|
18
20
|
raise UnknownRequestType, request.class.to_s unless klass
|
21
|
+
|
19
22
|
klass.new(request, options)
|
20
23
|
end
|
21
24
|
|
data/lib/oauth/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "oauth/helper"
|
2
4
|
require "oauth/consumer"
|
3
5
|
|
@@ -31,10 +33,12 @@ module OAuth
|
|
31
33
|
def create_consumer
|
32
34
|
creds = generate_credentials
|
33
35
|
Consumer.new(creds[0], creds[1],
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
{
|
37
|
+
site: base_url,
|
38
|
+
request_token_path: request_token_path,
|
39
|
+
authorize_path: authorize_path,
|
40
|
+
access_token_path: access_token_path
|
41
|
+
})
|
38
42
|
end
|
39
43
|
|
40
44
|
def request_token_path
|
data/lib/oauth/signature/base.rb
CHANGED
@@ -1,97 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "oauth/signature"
|
2
4
|
require "oauth/helper"
|
3
5
|
require "oauth/request_proxy/base"
|
4
6
|
require "base64"
|
5
7
|
|
6
|
-
module OAuth
|
7
|
-
|
8
|
-
|
8
|
+
module OAuth
|
9
|
+
module Signature
|
10
|
+
class Base
|
11
|
+
include OAuth::Helper
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
attr_accessor :options
|
14
|
+
attr_reader :token_secret, :consumer_secret, :request
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
@implements = signature_method
|
16
|
-
OAuth::Signature.available_methods[@implements] = self
|
17
|
-
end
|
16
|
+
def self.implements(signature_method = nil)
|
17
|
+
return @implements if signature_method.nil?
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@options = options
|
19
|
+
@implements = signature_method
|
20
|
+
OAuth::Signature.available_methods[@implements] = self
|
21
|
+
end
|
23
22
|
|
24
|
-
|
23
|
+
def initialize(request, options = {}, &block)
|
24
|
+
raise TypeError unless request.is_a?(OAuth::RequestProxy::Base)
|
25
25
|
|
26
|
-
|
26
|
+
@request = request
|
27
|
+
@options = options
|
27
28
|
|
28
|
-
|
29
|
-
@consumer_secret = options[:consumer_secret] if options[:consumer_secret]
|
29
|
+
## consumer secret was determined beforehand
|
30
30
|
|
31
|
-
|
31
|
+
@consumer_secret = options[:consumer].secret if options[:consumer]
|
32
32
|
|
33
|
-
|
33
|
+
# presence of :consumer_secret option will override any Consumer that's provided
|
34
|
+
if options[:consumer_secret]
|
35
|
+
@consumer_secret = options[:consumer_secret]
|
36
|
+
end
|
34
37
|
|
35
|
-
|
36
|
-
@token_secret = options[:token_secret] if options[:token_secret]
|
38
|
+
## token secret was determined beforehand
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
@token_secret = options[:token].secret if options[:token]
|
41
|
+
|
42
|
+
# presence of :token_secret option will override any Token that's provided
|
43
|
+
@token_secret = options[:token_secret] if options[:token_secret]
|
44
|
+
|
45
|
+
# override secrets based on the values returned from the block (if any)
|
46
|
+
if block
|
47
|
+
# consumer secret and token secret need to be looked up based on pieces of the request
|
48
|
+
secrets = yield block.arity == 1 ? request : [token, consumer_key, nonce, request.timestamp]
|
49
|
+
if secrets.is_a?(Array) && secrets.size == 2
|
50
|
+
@token_secret = secrets[0]
|
51
|
+
@consumer_secret = secrets[1]
|
52
|
+
end
|
45
53
|
end
|
46
54
|
end
|
47
|
-
end
|
48
55
|
|
49
|
-
|
50
|
-
|
51
|
-
|
56
|
+
def signature
|
57
|
+
Base64.encode64(digest).chomp.delete("\n")
|
58
|
+
end
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
def ==(other)
|
61
|
+
check = signature.bytesize ^ other.bytesize
|
62
|
+
signature.bytes.zip(other.bytes) { |x, y| check |= x ^ y.to_i }
|
63
|
+
check.zero?
|
64
|
+
end
|
58
65
|
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
def verify
|
67
|
+
self == request.signature
|
68
|
+
end
|
62
69
|
|
63
|
-
|
64
|
-
|
65
|
-
|
70
|
+
def signature_base_string
|
71
|
+
request.signature_base_string
|
72
|
+
end
|
66
73
|
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
def body_hash
|
75
|
+
raise_instantiation_error
|
76
|
+
end
|
70
77
|
|
71
|
-
|
78
|
+
private
|
72
79
|
|
73
|
-
|
74
|
-
|
75
|
-
|
80
|
+
def token
|
81
|
+
request.token
|
82
|
+
end
|
76
83
|
|
77
|
-
|
78
|
-
|
79
|
-
|
84
|
+
def consumer_key
|
85
|
+
request.consumer_key
|
86
|
+
end
|
80
87
|
|
81
|
-
|
82
|
-
|
83
|
-
|
88
|
+
def nonce
|
89
|
+
request.nonce
|
90
|
+
end
|
84
91
|
|
85
|
-
|
86
|
-
|
87
|
-
|
92
|
+
def secret
|
93
|
+
"#{escape(consumer_secret)}&#{escape(token_secret)}"
|
94
|
+
end
|
88
95
|
|
89
|
-
|
90
|
-
|
91
|
-
|
96
|
+
def digest
|
97
|
+
raise_instantiation_error
|
98
|
+
end
|
92
99
|
|
93
|
-
|
94
|
-
|
100
|
+
def raise_instantiation_error
|
101
|
+
raise NotImplementedError, "Cannot instantiate #{self.class.name} class directly."
|
102
|
+
end
|
95
103
|
end
|
96
104
|
end
|
97
105
|
end
|
@@ -1,17 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "oauth/signature/base"
|
2
4
|
|
3
|
-
module OAuth
|
4
|
-
|
5
|
-
|
5
|
+
module OAuth
|
6
|
+
module Signature
|
7
|
+
module HMAC
|
8
|
+
class SHA1 < OAuth::Signature::Base
|
9
|
+
implements "hmac-sha1"
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
def body_hash
|
12
|
+
Base64.encode64(OpenSSL::Digest.digest("SHA1", request.body || "")).chomp.delete("\n")
|
13
|
+
end
|
10
14
|
|
11
|
-
|
15
|
+
private
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
def digest
|
18
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha1"), secret, signature_base_string)
|
19
|
+
end
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -1,17 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "oauth/signature/base"
|
2
4
|
|
3
|
-
module OAuth
|
4
|
-
|
5
|
-
|
5
|
+
module OAuth
|
6
|
+
module Signature
|
7
|
+
module HMAC
|
8
|
+
class SHA256 < OAuth::Signature::Base
|
9
|
+
implements "hmac-sha256"
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
def body_hash
|
12
|
+
Base64.encode64(OpenSSL::Digest.digest("SHA256", request.body || "")).chomp.delete("\n")
|
13
|
+
end
|
10
14
|
|
11
|
-
|
15
|
+
private
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
def digest
|
18
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), secret, signature_base_string)
|
19
|
+
end
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -1,29 +1,27 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
class PLAINTEXT < Base
|
5
|
-
implements "plaintext"
|
3
|
+
require "oauth/signature/base"
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module OAuth
|
6
|
+
module Signature
|
7
|
+
class PLAINTEXT < Base
|
8
|
+
implements "plaintext"
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def signature
|
11
|
+
signature_base_string
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def body_hash
|
20
|
-
nil
|
21
|
-
end
|
14
|
+
def ==(other)
|
15
|
+
signature.to_s == other.to_s
|
16
|
+
end
|
22
17
|
|
23
|
-
|
18
|
+
def signature_base_string
|
19
|
+
secret
|
20
|
+
end
|
24
21
|
|
25
|
-
|
26
|
-
|
22
|
+
def body_hash
|
23
|
+
nil
|
24
|
+
end
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
@@ -1,50 +1,58 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
class SHA1 < OAuth::Signature::Base
|
5
|
-
implements "rsa-sha1"
|
3
|
+
require "oauth/signature/base"
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module OAuth
|
6
|
+
module Signature
|
7
|
+
module RSA
|
8
|
+
class SHA1 < OAuth::Signature::Base
|
9
|
+
implements "rsa-sha1"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
consumer_secret.public_key
|
16
|
-
else
|
17
|
-
consumer_secret
|
18
|
-
end
|
19
|
-
end
|
11
|
+
def ==(other)
|
12
|
+
public_key.verify(OpenSSL::Digest.new("SHA1"),
|
13
|
+
Base64.decode64(other.is_a?(Array) ? other.first : other), signature_base_string)
|
14
|
+
end
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
def public_key
|
17
|
+
case consumer_secret
|
18
|
+
when String
|
19
|
+
decode_public_key
|
20
|
+
when OpenSSL::X509::Certificate
|
21
|
+
consumer_secret.public_key
|
22
|
+
else
|
23
|
+
consumer_secret
|
24
|
+
end
|
25
|
+
end
|
24
26
|
|
25
|
-
|
27
|
+
def body_hash
|
28
|
+
Base64.encode64(OpenSSL::Digest.digest("SHA1", request.body || "")).chomp.delete("\n")
|
29
|
+
end
|
26
30
|
|
27
|
-
|
28
|
-
case consumer_secret
|
29
|
-
when /-----BEGIN CERTIFICATE-----/
|
30
|
-
OpenSSL::X509::Certificate.new(consumer_secret).public_key
|
31
|
-
else
|
32
|
-
OpenSSL::PKey::RSA.new(consumer_secret)
|
33
|
-
end
|
34
|
-
end
|
31
|
+
private
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
consumer_secret
|
33
|
+
def decode_public_key
|
34
|
+
case consumer_secret
|
35
|
+
when /-----BEGIN CERTIFICATE-----/
|
36
|
+
OpenSSL::X509::Certificate.new(consumer_secret).public_key
|
37
|
+
else
|
38
|
+
OpenSSL::PKey::RSA.new(consumer_secret)
|
39
|
+
end
|
44
40
|
end
|
45
|
-
)
|
46
41
|
|
47
|
-
|
42
|
+
def digest
|
43
|
+
private_key = OpenSSL::PKey::RSA.new(
|
44
|
+
if options[:private_key_file]
|
45
|
+
File.read(options[:private_key_file])
|
46
|
+
elsif options[:private_key]
|
47
|
+
options[:private_key]
|
48
|
+
else
|
49
|
+
consumer_secret
|
50
|
+
end
|
51
|
+
)
|
52
|
+
|
53
|
+
private_key.sign(OpenSSL::Digest.new("SHA1"), signature_base_string)
|
54
|
+
end
|
55
|
+
end
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
data/lib/oauth/signature.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth
|
2
4
|
module Signature
|
3
5
|
# Returns a list of available signature methods
|
@@ -15,6 +17,7 @@ module OAuth
|
|
15
17
|
((c = request.options[:consumer]) && c.options[:signature_method]) ||
|
16
18
|
"").downcase]
|
17
19
|
raise UnknownSignatureMethod, request.signature_method unless klass
|
20
|
+
|
18
21
|
klass.new(request, options, &block)
|
19
22
|
end
|
20
23
|
|
data/lib/oauth/token.rb
CHANGED