oauth 0.5.10 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -2
- data/README.md +60 -55
- data/SECURITY.md +7 -9
- 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 +45 -30
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